OAuth 2.0 integration guide

Implement OAuth 2.0 authentication in your Node.js application

This guide demonstrates how to implement OAuth 2.0 authentication in a Node.js application. Before starting, familiarize yourself with the OAuth 2.0 flow used at Brevo.

OAuth credentials

After your application is approved, you’ll receive the following credentials:

ItemDescriptionExample
client_idUnique identifier for your OAuth applicationmy-new-app
client_secretSecret token used to authenticate your application with the Brevo auth server3czT4bbqfmwN452B2VrqzO2iwTlJ452c
codeAuthorization code returned by the Brevo auth server after user authentication. JWT-based token with a 10-minute TTLab5a58c3-8ade-49ff-8f3c-e068548a478c.47a7dcff-3b84-43f4-8b32-99a9417896ba.5896f387-e86d-455d-a918-2fb8e8907936

Never hardcode credentials in client-facing code. Store them securely on the server side to prevent unauthorized access.

Create a redirect callback service

Create an Express service that handles callbacks from the Brevo auth server. This service must match the callback URL you specified in your application form.

1const express = require('express');
2const app = express();
3const port = 3000;
4
5app.get('/redirect', (req, res) => {
6 // Log the authorization code from the Brevo auth server
7 console.log(JSON.stringify(req.query.code));
8
9 // Return all parameters from the callback
10 res.json(JSON.stringify(req.query));
11});
12
13app.listen(port, () => {
14 console.log(`Server is running at http://localhost:${port}`);
15});

Define your login URL

Construct the authorization URL that redirects users to Brevo’s authentication page. Replace the following parameters:

  • client_id: Your OAuth application identifier
  • redirect_uri: Your callback URL that handles the redirect from the Brevo auth server
https://auth.brevo.com/realms/apiv3/protocol/openid-connect/auth?response_type=code&client_id={{YOUR_CLIENT_ID}}&redirect_uri={{YOUR_CALLBACK_URL}}&scope=openid

The URL is automatically encoded to protect your variables. After encoding, it may look like this:

https://auth.brevo.com/realms/apiv3/login-actions/authenticate?execution=8801c140-bd81-4f06-3251-8a5c0f54e7f9&client_id={{YOUR_CLIENT_ID}}&tab_id=e0h0VvmWuJM

Handle user authentication and callback

After a user successfully authenticates with Brevo, the auth server redirects to your redirect_uri with the authorization code and additional parameters.

Image

In your Express app running at http://localhost:3000, check the console for the code value.

The authorization code has a default TTL of 10 minutes. Exchange it for an access token before it expires.

Exchange code for access token

After receiving the authorization code, exchange it for an access token by calling the token endpoint. Use your client_id, client_secret, and the code from the callback.

$curl --location 'https://api.brevo.com/v3/token' \
> --header 'Content-Type: application/x-www-form-urlencoded' \
> --data-urlencode 'grant_type=authorization_code' \
> --data-urlencode 'client_id={{YOUR_CLIENT_ID}}' \
> --data-urlencode 'client_secret={{YOUR_CLIENT_SECRET}}' \
> --data-urlencode 'code={{YOUR_CODE}}' \
> --data-urlencode 'redirect_uri=http://localhost:3000/redirect'

The response contains your OAuth tokens:

1{
2 "access_token": "eyJhbGciOiJSU426234IsInR5cCIgOiAiSldUIiwia2lkIiA6ICJfb0pRamM0Q1EzaVByUUxTcWk2TEZmNGp6c0ZFdzlkWmhXaVVHMkExYkJnIn0.eyJleHAiOjE3MTk2MDk4MDAsImlhdCI6MTcxOTU2NjYwMCwiYXV0aF90aW1lIjoxNzE5NTY1NTE1LCJqdGkiOiJkY2M3OGRjYy1kYzViLTRlMmMtOWI1MS05NGZiOGZjMjM2YzAiLCJpc3MiOiJodHRwczovL2F1dGguYnJldm8uY29tL3JlYWxtcy9hcGl2MyIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiI1ZmM5Y2Y5MS01ODI5LTQyNDctODQwOS1lNjhhYjFmNTQ1MTYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJlbnZveS1hdXRoIiwic2Vzc2lvbl9zdGF0ZSI6IjQ3YTdkY2ZmLTNiODQtNDNmNC04YjMyLTk5YTk0ZDc4OTZiYSIsImFjciI6IjAiLCJhbGxvd2VkLW9yaWdpbnMiOlsiKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1hcGl2MyIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBtZXRhSW5mbyBwcm9maWxlIiwic2lkIjoiNDdhN2RjZmYtM2I4NC00M2Y0LThiMzItOTlhOTRkNzg5NmJhIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiTWF1cmljaW8gTW91cnJhaWxsZSIsInByZWZlcnJlZF91c2VybmFtZSI6Im1hdXJpY2lvQGJyZXZvLmNvbSIsImdpdmVuX25hbWUiOiJNYXVyaWNpbyIsImxvY2FsZSI6ImVuIiwiZmFtaWx5X25hbWUiOiJNb3VycmFpbGxlIiwiZW1haWwiOiJtYXVyaWNpb0BicmV2by5jb20ifQ.pROVoMSOGgbK88lbsHHUP2RtGRFOfmPIWCxI9UhErAm5Juu-PSGNI9sF4c52oPQjF2lx7Y3Nq8gc6jXE94T7WIS3hwRG0-saIJjqqTvYJnZ58PQXvkpRByezE6UPYpLUBdevpxyWSZV05OHGuMH6BYGmSYFoM92N8EtbPPaCzriPIoERhAKenDuO_E-7ZpxJGBwWDGOHkfj9O-fFwp-_ITuuID9bkp4kA7c5ctJ__k_Ll4MxxuoF3LZUyVGVPaXmZFOqTASQVzoV6nBm4vZNSA4qyqNno6DXAe1NpIHtcmPjPkk4LQgr38L7glXJtd47bZkdBqj0JGCUYuz2u01jLA",
3 "expires_in": 43200,
4 "refresh_expires_in": 31534915,
5 "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJiY2E0ODRjYS01ZTFmLTRiZDctYjM4NC1hNjA1YjhlZjAzZWYifQ.eyJleHAiOjE3NTExMDE1MTUsImlhdCI6MTcxOTU2NjYwMCwianRpIjoiZmM3Njg2NzMtODU3OC00MmE1LWJhZjMtY2UwZWJjNzVmOTcyIiwiaXNzIjoiaHR0cHM6Ly9hdXRoLmJyZXZvLmNvbS9yZWFsbXMvYXBpdjMiLCJhdWQiOiJodHRwczovL2F1dGguYnJldm8uY29tL3JlYWxtcy9hcGl2MyIsInN1YiI6IjVmYzljZjkxLTU4MjktNDI0Ny04NDA5LWU2OGFiMWY1NDUxNiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJlbnZveS1hdXRoIiwic2Vzc2lvbl9zdGF0ZSI6IjQ3YTdkY2ZmLTNiODQtNDNmNC04YjMyLTk5YTk0ZDc4OTZiYSIsInNjb3BlIjoib3BlbmlkIGVtYWlsIG1ldGFJbmZvIHByb2ZpbGUiLCJzaWQiOiI0N2E3ZGNmZi0zYjg0LTQzZjQtOGIzMi05OWE5NGQ3ODk2YmEifQ.mQLS52E_bYiYLBkYtQWPeGEUaCa8efJJD-wXUAya4rY",
6 "token_type": "Bearer",
7 "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJfb0pRamM0Q1EzaVByUUxTcWk2TEZmNGp6c0ZFdzlkWmhXaVVHMkExYkJnIn0.eyJleHAiOjE3MTk2MDk4MDAsImlhdCI6MTcxOTU2NjYwMCwiYXV0aF90aW1lIjoxNzE5NTY1NTE1LCJqdGkiOiIzZDA0ZTg4MS1iMmJkLTQxZWItOTc4Ni1lNWQ4OTAxZTdlNTUiLCJpc3MiOiJodHRwczovL2F1dGguYnJldm8uY29tL3JlYWxtcy9hcGl2MyIsImF1ZCI6ImVudm95LWF1dGgiLCJzdWIiOiI1ZmM5Y2Y5MS01ODI5LTQyNDctODQwOS1lNjhhYjFmNTQ1MTYiLCJ0eXAiOiJJRCIsImF6cCI6ImVudm95LWF1dGgiLCJzZXNzaW9uX3N0YXRlIjoiNDdhN2RjZmYtM2I4NC00M2Y0LThiMzItOTlhOTRkNzg5NmJhIiwiYXRfaGFzaCI6IndtVnl2UVdGN1Z5Q3pGZW9DZEU3SHciLCJhY3IiOiIwIiwic2lkIjoiNDdhN2RjZmYtM2I4NC00M2Y0LThiMzItOTlhOTRkNzg5NmJhIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiTWF1cmljaW8gTW91cnJhaWxsZSIsInByZWZlcnJlZF91c2VybmFtZSI6Im1hdXJpY2lvQGJyZXZvLmNvbSIsImdpdmVuX25hbWUiOiJNYXVyaWNpbyIsImxvY2FsZSI6ImVuIiwiZmFtaWx5X25hbWUiOiJNb3VycmFpbGxlIiwiZW1haWwiOiJtYXVyaWNpb0BicmV2by5jb20ifQ.b44PTMX6BMgTOI5_JyMs-zI3dhnrbJLpHeadta-OdsBGSOA8h4PGRuHLTvqsBEgXcW2OeyDpnpDcYK8GrNxiKP2fEbHaB1bRTNw4s2J_Qsicf27rj3WXQwD0WF1VrKiAIH3It9uuUpRifSlAwkmf-qwoWmHGNnCEGK7leYqALAhVHrvowolP-KG58W4LhqLSrIDsMYR8I25Oy3B8im_IgQv2_xxmsz-ZauiDUzyrpUQWiVbqzK_aENCSQD940CJzS52-W-4ukwguhev7ZnUFPKxVgWjV-I__a10bqX48IXMYN-5MzKwo4rlrqDTCVc6jOjXAY84EOOPd6jCmH9hbRQ",
8 "session_state": "47a7dcff-3b84-43f4-8b32-99a94d7896ba",
9 "scope": "openid email metaInfo profile"
10}

Make API requests with access token

Use the access_token in the Authorization header for all API requests. Include it as a Bearer token:

$curl --request GET \
> --url https://api.brevo.com/v3/account \
> --header 'accept: application/json' \
> --header 'Authorization: Bearer {{YOUR_ACCESS_TOKEN}}'

If the token is valid, the API returns your account details:

1{
2 "plan": [
3 {
4 "type": "subscription",
5 "credits": 39983,
6 "creditsType": "sendLimit",
7 "startDate": "2017-03-11",
8 "endDate": "2017-04-11"
9 }
10 ],
11 "relay": {
12 "enabled": true,
13 "data": {
14 "userName": "john.smith@example.com",
15 "relay": "smtp-relay.domain.com",
16 "port": 587
17 }
18 },
19 "marketingAutomation": {
20 "key": "kzfr5xxxxxxttuyo1",
21 "enabled": true
22 },
23 "email": "john.smith@example.com",
24 "firstName": "John",
25 "lastName": "Smith",
26 "companyName": "MyShop",
27 "address": {
28 "city": "New-York",
29 "street": "1677B 8th Avenue",
30 "zipCode": "7665",
31 "country": "USA"
32 }
33}

Use this access token format to authenticate requests to all Brevo API endpoints.