OAuth 2.0 for API Integrations

Connecting an AI agent to one SaaS API via OAuth 2.0 takes an afternoon. Connecting to ten takes months. Every provider implements the protocol differently enough that integration code written for Salesforce does not transfer to HubSpot, and neither transfers to Slack. In multi-tenant AI applications, those differences multiply across customers and quickly turn authentication into ongoing infrastructure that compounds with every new provider and every new customer.

TL;DR

  • Every SaaS provider makes different decisions about token lifetimes, refresh behavior, scope formats, and error responses, and no generic OAuth implementation covers them all.
  • Token refresh logic must be provider-specific because behaviors like HubSpot's refresh token rotation and Microsoft's 24-hour refresh requirement break generic refresh strategies.
  • Multi-tenant applications multiply this complexity: 200 customers connecting to 5 providers means 1,000 OAuth connections, each requiring isolated credentials, per-provider refresh scheduling, and revocation detection.
  • Building and maintaining per-provider OAuth code past 3–5 integrations costs more than adopting a managed connector platform.


How Does OAuth 2.0 Differ Across SaaS Providers?

OAuth 2.0 defines the protocol: the roles, the grant types, and the token exchange mechanism. Each SaaS provider then implements this protocol according to their own security model, API architecture, and product decisions.

The table below shows how far apart five common providers are on every major OAuth dimension.

OAuth Dimension Salesforce Google Workspace Slack HubSpot Microsoft 365
Access token lifetime ~2 hours default; session timeout admin-configured from 15 min to 24 hr ~1 hour Does not expire without rotation; 12 hours with rotation enabled 30 minutes 60–90 minutes, randomized
Refresh token behavior Issued by default; does not rotate Issued with offline access_type; does not rotate Not issued without rotation; both tokens rotate on refresh Issued by default; does not rotate or expire Issued for offline_access; replaces itself on every use; SPA tokens expire after 24 hours; 90-day max inactive
Scope format Granular strings URL-based Method-mapped Granular strings Dot-notation
Consent screen Custom approval; admin pre-authorization Consent screen; app verification Install flow with workspace approval Authorization per portal Admin consent; Conditional Access can block
Token revocation source User/admin settings; revoke endpoint Account/admin settings; API Admin UI; auth.revoke method Portal settings; v1 delete endpoint Admin/user portals; refresh tokens only
Error response format RFC-compliant at OAuth; custom at API Standard OAuth error responses Slack JSON with ok: false v3: RFC-compliant; v1: custom status/message format Standard plus AADSTS codes

These differences are not bugs in the protocol. OAuth 2.0 intentionally leaves many behaviors to the implementer's discretion: token lifetimes, refresh token policies, scope granularity, and error formats are all provider decisions. Every provider update risks breaking an integration that worked last month.

What Makes OAuth Hard When Integrating with Many APIs?

Each row in the table above looks manageable in isolation, but the differences interact: a scope misconfiguration triggers an error you cannot parse correctly because the error format is also provider-specific, and the resulting failed refresh invalidates a rotated token you cannot recover.

Token Refresh Logic Cannot Be Generic

An agent connecting to Salesforce, Google, Slack, HubSpot, and Microsoft 365 manages five different refresh schedules:

  • HubSpot access tokens expire every 30 minutes, though its refresh token does not rotate or expire, so the risk is frequency of refresh calls rather than credential loss
  • Google tokens expire hourly with stable refresh tokens that expire only after inactivity
  • Salesforce defaults to roughly 2-hour access tokens, but administrators configure session timeouts per organization from 15 minutes to 24 hours 
  • Microsoft randomizes access token lifetimes between 60 and 90 minutes, and refresh tokens replace themselves on every use with a 90-day inactivity maximum and a 24-hour hard limit for SPA-issued tokens
  • Slack tokens do not expire at all without rotation, but with rotation enabled, access tokens expire every 12 hours and both tokens rotate on refresh

A generic "refresh 5 minutes before expiration" strategy fails because it does not account for Slack's dual rotation or Microsoft's self-replacing refresh tokens. When Slack rotation is enabled, every refresh call issues a new refresh token and revokes the previous one. If the application fails to persist the new refresh token (a storage failure or a crash between receiving the response and writing to the database), the old refresh token is already revoked. 

The next refresh attempt fails, and no programmatic recovery exists. The user must re-authorize manually. Each provider needs its own refresh orchestration logic, tested against that provider's actual behavior rather than the OAuth specification's generic description.

Concurrent agent requests compound this further. Multiple requests hitting a token refresh simultaneously trigger race conditions where only the first refresh succeeds and others fail with invalid_grant. Preventing this requires single-flight refresh patterns with distributed locks: per-provider concurrency handling that no standard OAuth library provides.

Scope Vocabularies Are Incompatible Across Providers

An agent that reads contact data from multiple CRMs needs different scope strings for each:

  • Salesforce requires api (which grants broad API access with no object-level granularity)
  • HubSpot requires crm.objects.contacts.read
  • Google People API requires the https://www.googleapis.com/auth/contacts.readonly scope
  • Microsoft Graph requires Contacts.Read

These four scope strings follow four incompatible format conventions, from Salesforce's simple strings to HubSpot's dot-notation to Google's full URLs to Microsoft's own dot-notation with different casing rules.

The differences go deeper than format. 

Salesforce's full scope does not include refresh token capability. You must explicitly add refresh_token even when requesting full. HubSpot supports two scope formats simultaneously: legacy simple strings (contacts) and granular dot-notation (crm.objects.contacts.read). If you request the wrong scope, the token may still be issued but the API call returns a permissions error, or the authorization server rejects the request outright. 

Building a unified "read contacts" permission means mapping to the correct provider-specific scope for each CRM, abstraction work that shows up in every multi-provider integration and that no OAuth library provides.

Error Handling Is Provider-Specific

OAuth errors should follow a standard format: an error parameter with values like invalid_grant, invalid_scope, or access_denied. In practice, providers layer their own formats on top, and a parser built for one provider silently mishandles another.

  • Slack returns JSON with ok: false and an error string, so a standard parser that checks only the RFC error field format misses the failure entirely
  • Salesforce uses RFC-compliant errors at its OAuth endpoint but a different format with errorCode fields at its API layer
  • Microsoft returns standard OAuth fields plus an error_codes array with codes like 50076 (multi-factor authentication, or MFA, required) or 16000 (user doesn't exist in tenant)
  • HubSpot's deprecated v1 endpoints return a status/message structure while their newer v3 endpoints follow RFC 6749

Each provider needs its own response parser. A generic handler built for Google (the most RFC-compliant) will silently mishandle Slack, Salesforce API errors, and legacy HubSpot responses, which means failed API calls surface as mysterious data gaps rather than actionable errors.

How Do You Handle OAuth for Multi-Tenant AI Applications?

Multi-tenant AI applications add another dimension: each customer connects their own SaaS accounts. An AI agent serving 200 customers, each with Salesforce and Google Drive connected, manages 400 access tokens and 400 refresh tokens, each on its own expiration schedule with its own provider-specific refresh behavior.

The embeddable auth widget pattern addresses part of this complexity. Rather than building OAuth consent flows for each provider, multi-tenant applications embed a white-label widget that handles the entire OAuth flow per provider. The user clicks "Connect Salesforce," the widget manages the redirect, consent screen, token exchange, and credential storage. The agent application receives a connection identifier rather than raw tokens. This pattern abstracts per-provider OAuth differences behind a consistent interface and centralizes token lifecycle management.

Tenant isolation requirements add engineering overhead on top. Each customer's tokens must be stored separately, encrypted at rest (see OWASP guidance), and accessible only within that customer's context. OWASP recommends tenant isolation controls like including tenant_id in database queries and cache keys to prevent cross-tenant access. A token leak from one tenant must not compromise another tenant's connections. 

On top of isolation, all the per-provider refresh logic, rotation handling, and error parsing described above still applies, multiplied by customer count. Most teams discover around this point that the authentication layer consumes more engineering time than the agent logic it supports.

What Is the Most Practical Way to Handle OAuth Across Many API Integrations?

Stop writing per-provider OAuth code. After the third or fourth integration, the maintenance cost of custom refresh logic, scope mappings, and error parsers exceeds the development cost, and in multi-tenant applications, every provider-side update ripples across your entire customer base.

Airbyte's Agent Engine absorbs this complexity across 600+ supported connectors. The embeddable widget handles OAuth consent flows, token lifecycle, and credential storage per provider and per tenant. When providers change their implementations, Airbyte updates the connector. Your team writes agent logic, not auth infrastructure.

Talk to us to see how Airbyte's Agent Engine handles OAuth across agent connectors today. 

You build the agent. We'll bring the data.

Authenticate once. Fetch, search, and write in real-time.

Try Agent Engine →
Airbyte mascot


Frequently Asked Questions

Why do SaaS providers implement OAuth 2.0 differently?

OAuth 2.0 defines roles and grant types but intentionally leaves token lifetimes, scope formats, refresh policies, and consent requirements to each provider. This flexibility lets providers tailor OAuth to their security model, but it means integrators must handle each provider's specific choices individually.

What happens when a provider changes its OAuth implementation?

Providers update OAuth implementations regularly. HubSpot changed scopes over time, which can break existing installations silently, and Salesforce made a JWT change for PCI compliance, which can fail long-running operations mid-process. Managed connector platforms handle these updates within the connector and insulate the agent application from provider-side changes.

Can a single OAuth library handle multiple providers?

A library can handle the common OAuth flow: redirect, consent, token exchange, and refresh. Per-provider logic for scope vocabularies, rotation policies, and error formats must still be built separately. No major OAuth library provides multi-provider abstraction; the common flow covers roughly 40% of the total implementation.

When does a platform become worth it for OAuth connections?

The crossover point is typically 3–5 providers. Above that, the coordination overhead of different refresh schedules, incompatible scope vocabularies, provider-specific error handling, and the maintenance burden when providers ship OAuth updates exceeds the cost of a managed connector platform, especially in multi-tenant deployments where each customer multiplies connections across every provider.

What does OAuth 2.0 cover vs. data-level permissions?

OAuth 2.0 handles authorization, meaning which API endpoints and operations the token permits, but not data-level permissions. Providers like Salesforce, Google, and Microsoft independently restrict which data is returned through field-level security, organizational unit restrictions, and Conditional Access policies. Enforce data-level permissions separately, typically through row-level access controls applied at the retrieval layer.

Table of contents

Loading more...

Try the Agent Engine

We're building the future of agent data infrastructure. Be amongst the first to explore our new platform and get access to our latest features.