Supporting both Gmail and Outlook usually means two of everything: a Google Cloud project with its OAuth consent screen, an Azure app registration with admin consent, two sets of scope strings, two token-refresh loops, and two code paths that drift apart over time. For a multi-tenant SaaS app where every customer connects their own mailbox, that duplication compounds with each provider you add.
This recipe shows how one hosted authorization flow connects Gmail and Outlook through the same redirect URL, returns a single grant per user, and lets you read and send mail with identical calls regardless of which provider the user picked.
What is the best way to implement OAuth for Gmail and Outlook in the same app?
Section titled “What is the best way to implement OAuth for Gmail and Outlook in the same app?”The best way is to put one hosted OAuth flow in front of both providers, so your app never registers a Google Cloud project or Azure app itself. Configure a Google connector and a Microsoft connector once, then send every user through the same /v3/connect/auth redirect. Each flow returns one grant ID that works across all 6 supported providers.
This collapses the work that two raw integrations would duplicate. Instead of maintaining a Google OAuth client and an Azure registration with separate consent screens, scope lists, and token storage, you manage two connectors in one dashboard. The provider-specific OAuth machinery, including Google’s offline access type and Microsoft’s tenant consent, sits behind the connector. Your application code calls one authorization endpoint and stores one identifier per connected account, which is the difference between supporting two providers and supporting many.
How do I implement OAuth for Gmail and Outlook in the same app?
Section titled “How do I implement OAuth for Gmail and Outlook in the same app?”Redirect the user to /v3/connect/auth with a provider value of google or microsoft, let them approve access at their provider, then exchange the returned code for a grant at /v3/connect/token. The same two endpoints handle both providers, so the only thing that changes per user is the provider parameter you pass when you build the URL.
The request below starts the hosted flow for a Gmail account. It targets the authorization endpoint at /v3/connect/auth with your client ID, callback URI, and provider=google. Swap provider=microsoft and the identical request connects an Outlook account instead, which is what makes one flow cover both.
curl --request GET \ --url 'https://api.us.nylas.com/v3/connect/auth?client_id=<NYLAS_CLIENT_ID>&redirect_uri=https%3A%2F%2Fyourapp.com%2Fcallback&response_type=code&provider=google&[email protected]'curl --request GET \ --url 'https://api.us.nylas.com/v3/connect/auth?client_id=<NYLAS_CLIENT_ID>&redirect_uri=https%3A%2F%2Fyourapp.com%2Fcallback&response_type=code&provider=microsoft&[email protected]'After the user approves, the provider redirects back to your callback with a one-time code. You exchange that code at /v3/connect/token for the grant. The exchange request takes the code, your client_id, your API key in the client_secret field, the same redirect_uri, and grant_type=authorization_code. The response includes the grant_id you store against the user.
curl --request POST \ --url 'https://api.us.nylas.com/v3/connect/token' \ --header 'Content-Type: application/json' \ --data '{ "code": "<AUTHORIZATION_CODE>", "client_id": "<NYLAS_CLIENT_ID>", "client_secret": "<NYLAS_API_KEY>", "redirect_uri": "https://yourapp.com/callback", "grant_type": "authorization_code" }'Each code is single-use. If the exchange fails, the provider rejects a retry with the same code and you restart the flow. The hosted OAuth with an API key guide walks through the full exchange with runnable SDK examples for both providers.
How do I authenticate a multi-tenant SaaS app so each user connects their own account?
Section titled “How do I authenticate a multi-tenant SaaS app so each user connects their own account?”Send every user through the same hosted flow and use the state query parameter to track who’s connecting. Mint state as an unguessable, single-use token mapped to the user server-side, never a raw user ID, so an attacker can’t forge a callback. When your callback fires, look up the user by the returned state, then store that user’s grant_id. One redirect URL serves every tenant, and each flow produces a separate grant, so the pattern scales from 1 account to thousands.
The SDK builds the redirect URL for you. The urlForOAuth2 method takes your client ID, the provider the user chose, your callback URI, an optional loginHint that prefills their email address, and a random state token you map back to the user server-side. Generate the URL per user, set provider from their selection, then redirect.
const provider = user.choseOutlook ? "microsoft" : "google";
// Mint an unguessable, single-use state token and map it to the user server-sideconst state = crypto.randomBytes(16).toString("hex");await saveOAuthState(state, user.id);
const authUrl = nylas.auth.urlForOAuth2({ clientId: process.env.NYLAS_CLIENT_ID, provider, redirectUri: "https://yourapp.com/callback", loginHint: user.email, state,});
res.redirect(authUrl);To audit which accounts a tenant has connected, list grants with GET /v3/grants and filter by provider or grant_status. The default page size is 10 grants, and you can request valid grants only to skip ones the user or provider revoked. Setting provider=google or provider=microsoft shows how the connected mailboxes split across the two.
One flow versus separate Google Cloud and Azure projects
Section titled “One flow versus separate Google Cloud and Azure projects”Wiring Gmail and Outlook by hand means standing up and operating two unrelated OAuth integrations in parallel. A single hosted flow replaces both with one redirect URL, one token store you never touch, and one grant model. The table compares the work each approach needs for a multi-tenant app connecting both providers.
| Task | Separate Google Cloud + Azure projects | One hosted flow |
|---|---|---|
| Project setup | Google Cloud project plus Azure app registration | 1 Google connector and 1 Microsoft connector |
| Authorization endpoint | Google and Microsoft endpoints, different params | GET /v3/connect/auth with provider |
| Token exchange | Two token endpoints, two response shapes | POST /v3/connect/token for both |
| Token refresh | Two refresh loops you write and run | Automatic, no refresh code |
| Per-user identifier | Provider tokens you store and rotate | One grant_id, a non-secret value |
| Adding more providers | A new integration each time | Add a connector, reuse the same flow |
Token refresh is automatic in the unified model. The API stores each provider’s access and refresh tokens on the grant and refreshes them before they expire, so a long-lived Gmail or Outlook connection keeps working with no refresh logic in your app. A grant stays valid until the user or the provider revokes it, per the authentication best practices.
When should you build the OAuth flows yourself?
Section titled “When should you build the OAuth flows yourself?”Build the OAuth flows directly when your app is single-provider and depends on services beyond email and calendar. If you only ever connect Gmail and need Google Drive, Docs, or Workspace admin APIs, a Google Cloud project pays for itself and a unified layer would sit in the way. The same holds for a Microsoft-only app that needs Teams, SharePoint, or OneDrive through the Microsoft Graph API.
The math changes the moment you support both providers or only need mail and calendar. Two hand-built flows mean two consent screens to maintain, two scope lists to keep in sync, and two refresh loops to monitor, all for the same end result. Google’s OAuth verification for sensitive and restricted scopes, documented in the Google OAuth API verification FAQ, and Microsoft’s per-tenant admin consent each add review time before launch. One flow handles both consent models behind a single connector, so you ship once instead of twice.
What’s next
Section titled “What’s next”- Connect user accounts with OAuth for scopes, secure token storage, and revoking access
- OAuth scopes for email and calendar for the exact Gmail and Outlook scope strings
- Connect multiple accounts per user when one tenant links both a Gmail and an Outlook mailbox
- Hosted OAuth with an API key for the full token exchange on both providers