Only show these results:

Create grants with OAuth and an access token

This page describes how to use the OAuth 2.0 features included in Nylas v3 with an end user's access token to create a grant.

🔍 Nylas creates only one grant per email address in each application. If an end user authenticates with your Nylas application using the email address associated with an existing grant, Nylas re-authenticates the grant instead of creating a new one.

If you're using hosted authentication and are developing a Single Page App (SPA) or mobile app, Nylas recommends you use the OAuth 2.0 with PKCE authentication method for extra security. This extra layer of security adds a key to the authentication exchange that you can safely store on a mobile device instead of including the API key. This is optional, but a good idea for projects that have a backend.

Changes to OAuth in v3

Nylas v3 includes new OAuth 2.0 hosted authentication methods. These are similar to the v2.x hosted implementation in that the end user starts an authentication session and, upon successful authentication, Nylas receives an authentication token from the provider. Nylas then exchanges the authentication token for an access token.

OAuth access tokens expire hourly, so Nylas also gets a refresh token that it uses when re-authenticating. You can use an access token in requests for an end user's data, and use a refresh token to re-authenticate when the access token expires.

Using /me/ syntax to refer to a grant

Nylas v3 uses a new /me/ syntax that you use in access token-authorized API calls instead of specifying a user's grant ID (for example, GET /v3/grants/me/messages).

The /me/ syntax looks up the grant associated with the request's access token, and uses the grant_id associated with the token as a locator. You can use this syntax for API requests that access account data only, and only if you use access tokens to authorize requests. You can't use this syntax if you're using API key authorization, because there is no grant associated with an API key.

For more information, see /me/ syntax for API calls.

Before you begin

Before you begin, make sure you set up all the required parts for your authentication system:

  1. If you haven't already, log in to the v3 Dashboard and create a Nylas application.
  2. Generate an API key for your application in the v3 Dashboard.
  3. Create a provider auth app in the provider's console or application. See the detailed instructions for creating a Google or Azure provider auth app.
  4. Create a connector for the provider you want to authenticate with.
  5. Add your project's callback URIs ("redirect URIs") in the Nylas Dashboard.

Create grants with OAuth and an access token

📝 Note: Because Nylas uses the schema outlined in RFC 9068 to ensure that it is compatible with all OAuth libraries in all languages, the format for the /tokeninfo endpoint is different from the other OAuth endpoints.

Make authorization request

The first step of the OAuth process is to collect the information you need to include when starting an authorization request. You usually start the request using either a button or link that the end user clicks, which redirects them to api.us.nylas.com/v3/connect/auth and includes their information. The example below requests two scopes using a Google provider auth app.

/v3/connect/auth?
client_id=<NYLAS_CLIENT_ID>
&redirect_uri=https://myapp.com/callback-handler
&response_type=code
&provider=google
&access_type=offline
&state=sQ6vFQN

This request also uses the optional access_type=offline parameter to specify that we do want to receive a refresh token. The rendered URL that the user is directed to will look similar to the example below.

https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?
client_id=<GCP_CLIENT_ID> // The client ID from your Google auth app.
&prompt=consent
&redirect_uri=https://accounts.nylas.com/connect/callback
&response_type=code
&scope=https://www.googleapis.com/auth/gmail.readonly%20profile
&access_type=offline
&state=BA630DED06... // Stored and checked/compared internally by Nylas for security.

You can also start the authorization process using the Nylas SDKs, as in the examples below.

import 'dotenv/config'
import express from 'express'
import Nylas from 'nylas'

const config = {
clientId: process.env.NYLAS_CLIENT_ID,
callbackUri: "http://localhost:3000/oauth/exchange",
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}

const nylas = new Nylas({
apiKey: config.apiKey,
apiUri: config.apiUri,
})

const app = express()
const port = 3000

// Route to initialize authentication.
app.get('/nylas/auth', (req, res) => {
const authUrl = nylas.auth.urlForOAuth2({
clientId: config.clientId,
provider: 'google',
redirectUri: config.redirectUri,
loginHint: 'email_to_connect',
accessType: 'offline',
})

res.redirect(authUrl)
})
from dotenv import load_dotenv
load_dotenv()

import os
from functools import wraps
from flask import Flask, request, redirect
from nylas import Client

nylas = Client(
os.environ.get("NYLAS_CLIENT_ID"),
os.environ.get("NYLAS_API_URI")
)

REDIRECT_CLIENT_URI = 'http://localhost:9000/oauth/exchange'

flask_app = Flask(__name__)

@flask_app.route("/nylas/generate-auth-url", methods=["GET"])

def build_auth_url():
auth_url = nylas.auth.url_for_oauth2(
config={
"client_id": os.environ.get("NYLAS_CLIENT_ID"),
"provider": 'google',
"redirect_uri": REDIRECT_CLIENT_URI,
"login_hint": "enter-email-address-here",
"access_type": "offline",
}
)
return redirect(auth_url)
# frozen_string_literal: true

require 'nylas'
require 'sinatra'

set :show_exceptions, :after_handler

error 404 do
'No authorization code returned from Nylas'
end

error 500 do
'Failed to exchange authorization code for token'
end

nylas = Nylas::Client.new(
api_key: ENV['NYLAS_API_KEY']
)

get '/nylas/auth' do
config = {
client_id: "<NYLAS_CLIENT_ID>",
provider: 'google',
redirect_uri: 'http://localhost:4567/oauth/exchange',
login_hint: '<email_to_connect>',
accessType: 'offline',
}

url = nylas.auth.url_for_oauth2(config)
redirect url
end
import java.util.*;
import static spark.Spark.*;
import com.nylas.NylasClient;
import com.nylas.models.*;

public class AuthRequest {
public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();

get("/nylas/auth", (request, response) -> {
List<String> scope = new ArrayList<>();
scope.add("https://www.googleapis.com/auth/userinfo.email");

UrlForAuthenticationConfig config = new UrlForAuthenticationConfig("<NYLAS_CLIENT_ID>",
"http://localhost:4567/oauth/exchange",
AccessType.OFFLINE,
AuthProvider.GOOGLE,
Prompt.DETECT,
scope,
true,
"sQ6vFQN",
"<email_to_connect>");

String url = nylas.auth().urlForOAuth2(config);

response.redirect(url);

return null;
});
}
}
import com.nylas.NylasClient
import com.nylas.models.AccessType
import com.nylas.models.AuthProvider
import com.nylas.models.Prompt
import com.nylas.models.UrlForAuthenticationConfig
import spark.kotlin.Http
import spark.kotlin.ignite

fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(
apiKey = "<NYLAS_API_KEY>"
)

val http: Http = ignite()

http.get("/nylas/auth") {
val scope = listOf("https://www.googleapis.com/auth/userinfo.email")

val config : UrlForAuthenticationConfig = UrlForAuthenticationConfig(
"<NYLAS_CLIENT_ID>",
"http://localhost:4567/oauth/exchange",
AccessType.OFFLINE,
AuthProvider.GOOGLE,
Prompt.DETECT,
scope,
true,
"sQ6vFQN",
"<email_to_connect>"
)

val url = nylas.auth().urlForOAuth2(config)

response.redirect(url)
}
}

The user goes to the URL Nylas starts a secure authentication session and redirects the user to the provider website.

Each provider displays the consent and approval steps differently, and it's only visible to the end-user. In all cases the user authenticates, then either accepts or declines the scopes your project requested.

🔍 If an end user authenticates using their Google account, they might be directed to Google's authorization screen twice. This is a normal part of Nylas' Hosted auth flow, and ensures that all necessary scopes are approved during the auth process.

Authorization response and grant creation

Next, the auth provider sends the user to the Nylas redirect_uri (https://api.us.nylas.com/v3/connect/callback), and includes information about the outcome of the session as query parameters in the URL.

Nylas uses the information in the response to find your Nylas application by client_id and, if the auth was successful, create an unverified grant record for the end user and record their details.

Get the user's code

Nylas uses your application's callback_uri (for example, app.example.com/callback-handler) to forward the end user back to your project. The callback URI includes query parameters to indicate to your project if authentication was successful.

The example below shows parameters for a successful authentication. The code and state query parameters are standard OAuth 2.0 fields, but Nylas provides some optional fields to give you more context about the authentication.

https://myapp.com/callback-handler?
state=... // Passed value of initial state if it was provided.
&code=... // Use this code value for the next step of authentication.

Exchange code for access token

Next, exchange the code for an access token. Make a /v3/connect/token request and include the the code parameter from the success response.

POST /token HTTP/1.1
Host: /v3/connect/token
Content-Type: application/json

{
"code": "<code>",
"client_id": "<NYLAS_CLIENT_ID>",
"client_secret": "<NYLAS_API_KEY>",
"redirect_uri": "<application_redirect_uri>",
"grant_type": "authorization_code"
}

The auth provider responds with an access token, a refresh token (because you asked for one by setting access_type=offline when you made the authorization request), and some other information. When this is successfully completed, Nylas marks the end user's grant as verified and sends you their grant_id and email address.

Your application should store the grant_id, the access_token, and refresh_token (for later re-authentication).

{
"access_token": "<ACCESS_TOKEN>",
"refresh_token": "<REFRESH_TOKEN>",
"scope": "https://www.googleapis.com/auth/gmail.readonly profile",
"token_type": "Bearer",
"id_token": "<id_token>",
"grant_id": "<grant_id>"
}

You can also exchange the end user's code using the Nylas SDKs.

app.get('/oauth/exchange', async (req, res) => {
console.log(res.status)

const code = req.query.code

if (!code) {
res.status(400).send('No authorization code returned from Nylas')
return
}

try {
const response = await nylas.auth.exchangeCodeForToken({
clientId: config.clientId,
redirectUri: config.redirectUri,
codeVerifier: 'insert-code-challenge-secret-hash',
code
})

const { grantId } = response

res.status(200)
} catch (error) {
console.error('Error exchanging code for token:', error)

res.status(500).send('Failed to exchange authorization code for token')
}
})
from dotenv import load_dotenv
load_dotenv()

import json
import os
from functools import wraps
from io import BytesIO
from flask import Flask
from nylas import Client

nylas = Client(
os.environ.get("NYLAS_CLIENT_ID"),
os.environ.get("NYLAS_API_URI")
)

REDIRECT_CLIENT_URI = 'http://localhost:9000/oauth/exchange'

@flask_app.route("/oauth/exchange", methods=["GET"])

def exchange_code_for_token():
code_exchange_response = nylas.auth.exchange_code_for_token(
request={
"code": request.args.get('code'),
"client_id": os.environ.get("NYLAS_CLIENT_ID"),
"redirect_uri": REDIRECT_CLIENT_URI
}
)

return {
'email_address': code_exchange_response.email,
'grant_id': code_exchange_response.grant_id
}
get '/oauth/exchange' do
code = params[:code]
status 404 if code.nil?

begin
response = nylas.auth.exchange_code_for_token({
client_id: "<NYLAS_CLIENT_ID>",
redirect_uri: 'http://localhost:4567/oauth/exchange',
code: code
})
rescue StandardError
status 500
else
grant_id = response[:grant_id]
email = response[:email]

"Grant_Id: #{grant_id} \n Email: #{email}"
end
end
get("/oauth/exchange", (request, response) -> {
String code = request.queryParams("code");

if(code == null) { response.status(401); }

assert code != null;

CodeExchangeRequest codeRequest = new CodeExchangeRequest(
"http://localhost:4567/oauth/exchange",
code,
"<NYLAS_CLIENT_ID>",
"nylas"
);

try {
CodeExchangeResponse codeResponse = nylas.auth().exchangeCodeForToken(codeRequest);

return "%s".formatted(codeResponse);
} catch(Exception e) {
return "%s".formatted(e);
}
});
http.get("/oauth/exchange") {
val code : String = request.queryParams("code")

if(code == "") { response.status(401) }

val codeRequest : CodeExchangeRequest = CodeExchangeRequest(
"http://localhost:4567/oauth/exchange",
code,
"<NYLAS_CLIENT_ID>",
"nylas"
)

try {
val codeResponse : CodeExchangeResponse = nylas.auth().exchangeCodeForToken(codeRequest)

codeResponse
} catch (e : Exception) {
e
}
}

Authorize API calls with access token and /me/ syntax

When you have an end user's access token, you can use it to authorize API requests for that user's data, and that user's data only. You cannot use an access token to authorize API requests that access or modify data at the application level. Those requests require an API key for authorization.

To authorize an API request, pass the token in the request header using HTTP Bearer authentication, then substitute the word me in the API calls where you would usually specify a grant_id.

When Nylas receives a request using the /me/ syntax, it checks the authorization header token, finds the grant_id associated with that token, and uses that ID to locate data for the user.

The examples below illustrate an API request using an access token and the /me/ syntax, and the equivalent call using the grant_id.

curl --request GET \
--url 'https://api.us.nylas.com/v3/grants/me/calendars' \
--header 'Authorization: Bearer <ACCESS_TOKEN>' \
--header 'Content-Type: application/json'
curl --request GET \
--url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/calendars' \
--header 'Authorization: Bearer <NYLAS_API_KEY>' \
--header 'Content-Type: application/json'

(Optional) Refresh an expired access token

If you set access_type=offline in the initial authorization request, Nylas returns a refresh_token along with the access_token during the token exchange. When the initial access_token expires, you can use the refresh_token to request a new one.

🔍 Refresh tokens do not expire unless revoked. If your application is client-side-only, you shouldn't request offline access or need this step.

Make a /v3/connect/token request that specifies "grant_type": "refresh_token" and includes the refresh token, as in the example below. The auth provider returns a fresh access token.

POST /token HTTP/1.1
Host: /v3/connect/token
Content-Type: application/json

{
"client_id": "<NYLAS_CLIENT_ID>",
"grant_type": "refresh_token",
"refresh_token": "<REFRESH_TOKEN>"
}
{
"access_token": "<ACCESS_TOKEN>",
"scope": "https://www.googleapis.com/auth/gmail.readonly profile",
"token_type": "Bearer"
}

You can also use the Nylas SDKs to refresh an access token.

const config = {
clientId: process.env.NYLAS_CLIENT_ID,
callbackUri: "http://localhost:3000/oauth/exchange",
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI
}

const nylas = new Nylas({
apiKey: config.apiKey,
apiUri: config.apiUri
})

const refreshed = await nylas.auth.refreshAccessToken({
clientId: config.clientId,
refreshToken: response.refreshToken,
redirectUri: config.redirectUri
})
from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
os.environ.get('NYLAS_API_KEY'),
os.environ.get('NYLAS_API_URI')
)

REDIRECT_CLIENT_URI = 'http://localhost:9000/oauth/exchange'

response = nylas.auth.refresh_access_token(
request={
"client_id": os.environ.get("NYLAS_CLIENT_ID"),
"refresh_token": '<NYLAS_REFRESH_TOKEN>',
"redirect_uri": REDIRECT_CLIENT_URI
}
)
get '/nylas/refresh' do
request = {
clientId: "<NYLAS_CLIENT_ID>",
refreshToken: "<NYLAS_REFRESH_TOKEN>",
redirectUri: "http://localhost:4567/oauth/exchange"
}

refreshed_token = nylas.auth.refresh_access_token(request)
end
get("/nylas/refresh", (request, response) -> {
TokenExchangeRequest token = new TokenExchangeRequest(
"http://localhost:4567/oauth/exchange",
"<NYLAS_REFRESH_TOKEN>",
"<NYLAS_CLIENT_ID>"
);

return nylas.auth().refreshAccessToken(token);
});
http.get("/nylas/refresh") {
val token = TokenExchangeRequest(
"http://localhost:4567/oauth/exchange",
"<NYLAS_REFRESH_TOKEN>",
"<NYLAS_API_KEY>"
)

nylas.auth().refreshAccessToken(token)
}

Create grants with OAuth 2.0 and PKCE

The OAuth PKCE (Proof Key for Code Exchange) flow improves security for client-side-only applications, such as browser-based and mobile applications that don't have a backend server.

For security reasons, you should not store an application-wide credentials like the API key in your client-side code. Instead, you want to complete the code exchange flow without using your Nylas application's API key.

Even if your application does have a backend, Nylas still recommends that you use PKCE for extra security.

Make a connect request with a code verifier

The first step for OAuth with PKCE is the same as without PKCE: make a GET /v3/connect/auth request to generate a URL to redirect the end user. It will look something like the example below. Note the long code_challenge string and code_challenge_method included in the call.

/connect/auth?
client_id=<NYLAS_CLIENT_ID>
&redirect_uri=https://myapp.com/callback-handler
&response_type=code
&provider=google
&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly%20profile
&state=sQ6vFQN
&code_challenge=ZTk2YmY2Njg2YTNjMzUxMGU5ZTkyN2RiNzA2OWNiMWNiYTliOTliMDIyZjQ5NDgzYTZjZTMyNzA4MDllNjhhMg
&code_challenge_method=S256

You can also make a connect request using the Nylas SDKs, as in the following examples.

import 'dotenv/config'
import express from 'express'
import Nylas from 'nylas'

// Nylas configuration
const config = {
clientId: process.env.NYLAS_CLIENT_ID,
redirectUri: "http://localhost:3000/oauth/exchange",
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.SERVER_URL,
}

const config = {
apiKey: config.apiKey,
apiUri: config.apiUri,
}

const nylas = new Nylas(config)

const app = express()
const port = 3000

// Route to start the OAuth flow
app.get('/nylas/auth', (req, res) => {
const authData = nylas.auth.urlForOAuth2PKCE({
clientId: config.clientId,
provider: 'google',
redirectUri: config.redirectUri,
loginHint: 'enter-email-address-here',
})

res.redirect(authData.url)
})
from dotenv import load_dotenv
load_dotenv()

import json
import os
from functools import wraps

from io import BytesIO
from flask import Flask, redirect
from flask_cors import CORS

from nylas import Client

nylas = Client(
os.environ.get("NYLAS_CLIENT_ID"),
os.environ.get("NYLAS_API_URI")
)

REDIRECT_CLIENT_URI = 'http://localhost:9000/oauth/exchange'

flask_app = Flask(__name__)

@flask_app.route("/nylas/generate-auth-url", methods=["GET"])
def build_auth_url():
auth_url = nylas.auth.url_for_oauth2_pkce(
config={
"client_id": os.environ.get("NYLAS_CLIENT_ID"),
"provider": 'google',
"redirect_uri": REDIRECT_CLIENT_URI,
"login_hint": 'enter-email-address-here'
}
)

return redirect(auth_url)
# frozen_string_literal: true

require 'nylas'
require 'sinatra'

set :show_exceptions, :after_handler

error 404 do
'No authorization code returned from Nylas'
end

error 500 do
'Failed to exchange authorization code for token'
end

nylas = Nylas::Client.new(
api_key: "<NYLAS_API_KEY>"
)

get '/nylas/auth' do
config = {
client_id: "<NYLAS_CLIENT_ID>",
provider: 'google',
redirect_uri: 'http://localhost:4567/oauth/exchange',
login_hint: '<email_to_connect>',
}

authData = nylas.auth.url_for_oauth2_pkce(config)
redirect authData.url
end
import java.util.*;
import static spark.Spark.*;
import com.nylas.NylasClient;
import com.nylas.models.*;

public class AuthRequest {
public static void main(String[] args) throws NylasSdkTimeoutError, NylasApiError {
NylasClient nylas = new NylasClient.Builder("<NYLAS_API_KEY>").build();

get("/nylas/auth", (request, response) -> {
List<String> scope = new ArrayList<>();
scope.add("https://www.googleapis.com/auth/userinfo.email");

UrlForAuthenticationConfig config = new UrlForAuthenticationConfig("<GCP_CLIENT_ID>",
"http://localhost:4567/oauth/exchange",
AccessType.ONLINE,
AuthProvider.GOOGLE,
Prompt.DETECT,
scope,
true,
"sQ6vFQN",
"[email protected]");

PKCEAuthURL authData = nylas.auth().urlForOAuth2PKCE(config);

response.redirect(authData.getUrl());

return null;
});
}
}
import com.nylas.NylasClient
import com.nylas.models.*
import spark.kotlin.Http
import spark.kotlin.ignite

fun main(args: Array<String>) {
val nylas: NylasClient = NylasClient(
apiKey = "<NYLAS_API_KEY>"
)

val http: Http = ignite()

http.get("/nylas/auth") {
val scope = listOf("https://www.googleapis.com/auth/userinfo.email")

val config : UrlForAuthenticationConfig = UrlForAuthenticationConfig(
"<GCP_CLIENT_ID>",
"<GCP_CLIENT_SECRET>",
AccessType.ONLINE,
AuthProvider.GOOGLE,
Prompt.DETECT,
scope,
true,
"sQ6vFQN",
"<email_to_connect>"
)

var authData = nylas.auth().urlForOAuth2PKCE(config)

response.redirect(authData.url)
}
}

Constructing a Code Challenge

The following example uses the nylas code verifier string, but sets the method to S256 instead of plain for extra security. If you use the plain method, the code_challenge value is nylas as a regular string.

Because this uses SHA256, you take the code verifier string, hash it using SHA256, convert it to Base64 encoding, then set the code_challenge parameter to the resulting value.

This example sets the code verifier string to nylas, which is e96bf6686a3c3510e9e927db7069cb1cba9b99b022f49483a6ce3270809e68a2 when SHA256 hashed:

SHA256("nylas") => "e96bf6686a3c3510e9e927db7069cb1cba9b99b022f49483a6ce3270809e68a2"

When you convert it to Base64 encoding (and remove the padding) it becomes ZTk2YmY2Njg2YTNjMzUxMGU5ZTkyN2RiNzA2OWNiMWNiYTliOTliMDIyZjQ5NDgzYTZjZTMyNzA4MDllNjhhMg.

Exchange authorization code for access token

The rest of the OAuth flow should proceed as usual: the end user goes to the auth provider, authenticates and accepts or rejects the requested scopes, the provider sends the user back to Nylas with a code, Nylas creates an unverified grant and returns the user to your project with a code to exchange for an access token.

Use the code parameter in a /v3/connect/token request to exchange tokens, but set the grant_type to authorization_code, and include the code_verifier string.

For readability, the example below sets code_verifier to the original plain text code_verifier value.

POST /token HTTP/1.1
Host: /v3/connect/token
Content-Type: application/json

{
"code": "<code>",
"client_id": "<NYLAS_CLIENT_ID>",
"redirect_uri": "<APPLICATION_REDIRECT_URI>",
"grant_type": "authorization_code",
"code_verifier":"nylas"
}

⚠️ You must include your Nylas application's API key to refresh your access token. If you are working on a client-side Javascript or mobile application, you can use grant_type=authorization_code instead of grant_type=refresh_token so you don't need to store the API key.

You can also exchange the code for an access token using the Nylas SDKs, as in the following examples.

app.get('/oauth/exchange', async (req, res) => {
console.log(res.status)

const code = req.query.code

if (!code) {
res.status(400).send('No authorization code returned from Nylas')
return
}

try {
const response = await nylas.auth.exchangeCodeForToken({
clientId: config.clientId,
redirectUri: config.redirectUri,
codeVerifier: 'insert-code-challenge-secret-hash',
code
})

const { grantId } = response

res.status(200)
} catch (error) {
console.error('Error exchanging code for token:', error)

res.status(500).send('Failed to exchange authorization code for token')
}
})
from dotenv import load_dotenv
load_dotenv()

import json
import os
from functools import wraps
from io import BytesIO
from flask import Flask
from nylas import Client

nylas = Client(
os.environ.get("NYLAS_CLIENT_ID"),
os.environ.get("NYLAS_API_URI")
)

REDIRECT_CLIENT_URI = 'http://localhost:9000/oauth/exchange'

@flask_app.route("/oauth/exchange", methods=["GET"])

def exchange_code_for_token():
code_exchange_response = nylas.auth.exchange_code_for_token(
request={
"code": request.args.get('code'),
"client_id": os.environ.get("NYLAS_CLIENT_ID"),
"redirect_uri": REDIRECT_CLIENT_URI
}
)

return {
'email_address': code_exchange_response.email,
'grant_id': code_exchange_response.grant_id
}
set :show_exceptions, :after_handler

error 404 do
'No authorization code returned from Nylas'
end

error 500 do
'Failed to exchange authorization code for token'
end

get '/oauth/exchange' do
code = params[:code]
status 404 if code.nil?

begin
response = nylas.auth.exchange_code_for_token({
client_id: "<NYLAS_CLIENT_ID>",
redirect_uri: 'http://localhost:4567/oauth/exchange',
code_verifier: 'insert-code-challenge-secret-hash',
code: code
})
rescue StandardError
status 500
else
grant_id = response[:grant_id]
email = response[:email]

"Grant_Id: #{grant_id} \n Email: #{email}"
end
end
get("/oauth/exchange", (request, response) -> {
String code = request.queryParams("code");

if(code == null) { response.status(401); }

assert code != null;

CodeExchangeRequest codeRequest = new CodeExchangeRequest(
"http://localhost:4567/oauth/exchange",
code,
"<NYLAS_CLIENT_ID>",
"<CODE_CHALLENGE>"
);

try {
CodeExchangeResponse codeResponse = nylas.auth().exchangeCodeForToken(codeRequest);

return "%s".formatted(codeResponse);
} catch(Exception e) {
return "%s".formatted(e);
}
});
http.get("/oauth/exchange") {
val code : String = request.queryParams("code")

if(code == "") { response.status(401) }

val codeRequest : CodeExchangeRequest = CodeExchangeRequest(
"http://localhost:4567/oauth/exchange",
code,
"<NYLAS_CLIENT_ID>",
"<CODE_CHALLENGE>"
)

try {
val codeResponse : CodeExchangeResponse = nylas.auth().exchangeCodeForToken(codeRequest)

codeResponse
} catch (e : Exception) {
e
}
}

Handling authentication errors

If authentication fails, Nylas returns the standard OAuth 2.0 error fields in the response: error, error_description, and error_uri.

https://myapp.com/callback-handler?
state=... // Passed value of initial state if it was provided
&error=... // Error type/constant
&error_description=... // Error description
&error_uri=... // Error or event code

If an unexpected error occurs during the callback URI creation step at the end of the flow, the response includes an error_code field instead of an error_uri.

https://myapp.com/callback-handler?
&error=internal_error // Error type/constant
&error_description=Internal+error%2C+contact+administrator // Error description
&error_code=500 // Code of internal error

What's next?

Now that you have a connector and you've received a grant, you can browse the following documentation to learn more.