Unified.to
All articles

How We Normalize OAuth Across 280+ APIs at Unified.to


June 20, 2025

OAuth 2.0 is a standard, but it rarely feels that way when you're building integrations across 280+ SaaS platforms. Each one introduces a new edge case: different permission scopes, redirect flow rules, content types, token result formats, and error responses. At Unified.to, our goal is to remove that complexity entirely.

This post walks through how Unified.to normalizes OAuth across hundreds of APIs, how we expose that to developers through our SDK, and what patterns we've adopted to abstract away common OAuth pitfalls.

Why OAuth Fragmentation Slows Teams Down

If you've ever built integrations with multiple third-party APIs, you've probably encountered:

  • One provider using client_credentials, another authorization_code, a third relying on legacy tokens
  • Scope naming collisions: read_contacts vs. contacts.read vs. read_people
  • Redirect URI mismatch errors due to minor differences in path formatting
  • HTML errors instead of JSON on expired tokens
  • Varying token formats, expiration policies, and re-consent behaviors
  • So many refresh strategies; refreshing access token, refreshing the refresh token, time-based and activity-based expiries, re-authentication expiries, …

Each new integration adds more code paths, more exception handling, and more time spent maintaining brittle OAuth logic. In fact, at Unified.to, we've found (and support) more than 70 variations of the OAuth2 "standard".

Our Design Goals

Unified.to was built to abstract this away. We focused on:

  1. Supporting all major OAuth grant types & flows
  2. Standardizing redirect URIs across every integration
  3. Mapping provider scopes to a unified set
  4. Handling token exchange, refresh, and revocation uniformly
  5. Surfacing OAuth errors in a consistent way
  6. Giving developers a single method to initiate and monitor authorization flows

Here's how we've implemented those.

1. Grant Type Normalization

Unified.to supports authorization_code, client_credentials, password and refresh_token flows. We also support API key/token-based authorization where applicable. For each integration, we define the required grant type and credentials internally. Developers only need to call unified.connect() in our SDK.

Under the hood, we initiate the appropriate flow, handle redirects, and capture tokens. We also expose these tokens to your infrastructure via secure storage, or store them encrypted and scoped per connection.

2. Unified Redirect URIs

We use a single redirect URI across all integrations: https://api.unified.to/oauth/code (plus EU and AU versions). Providers are configured to use that during app registration.

That means no environment-specific redirects, no per-provider callback config, and fewer deployment issues. This one endpoint handles all OAuth code exchanges.

3. Unified Scope Abstraction

Instead of asking developers to memorize each provider's scope strings, we maintain a library of unified scopes (e.g. crm_contact_read, storage_file_write, calendar_event_read).

In the Unified.to SDK or dashboard, you select these abstract scopes. We map them to provider-specific scopes under the hood.

Need a custom scope? You can pass provider-native scope strings too. Unified.to will merge and handle them.

4. Token Management and Refresh

Once tokens are obtained, Unified.to:

  • Stores them encrypted or in your own AWS Secrets Manager
  • Tracks expiration
  • Refreshes access tokens and, if needed, refresh tokens automatically
  • Handles provider-specific quirks (e.g. rolling refresh, re-consent windows)

This means no cron jobs, no manual expiry tracking, and no customer disruption. If a refresh fails, Unified.to flags the connection and surfaces a consistent 401 or needs_reconnect error and sends your server an event notification.

5. Standardized Error Surfacing

Every provider returns errors differently. Unified.to standardizes them:

  • 401 Unauthorized: Token expired or revoked
  • 403 Forbidden: Missing scope or permission
  • 400 Bad Request: Invalid config or payload

The authorization also will test the newly created connection to ensure that it has access to the data that your permissions requested. If it doesn't then a 403 error is returned and the connection is not created.

In every OAuth failure (auth rejection, scope issue, redirect error), we surface an error object in a consistent format. Developers only have to handle a known set of cases.

6. One SDK, One Interface

Our SDK exposes a single connect() method for initiating OAuth, and a getConnection() method for retrieving status and metadata.

Each connection object includes:

  • Connection ID
  • Status (healthy, unhealthy, needs_reconnect)
  • Scopes granted
  • Refresh history
  • Error state if present

This simplifies frontend UI and backend sync logic. No per-integration code required.

7. Pre-build Authorization UI

To make it dead-simple to get your customers to authorize access to their accounts, we've built UI components that you can use directly inside your application with just 1 line of code.

  1. We have pre-build components for React, Angular, VueJS, Svelte and Javascript.
  2. Plus you can fork our publicly available source code and redesign it however you like.
  3. If that isn't enough customization, then just use our API to get a list of activated integrations which will include a logo URL, name, and authorization URL, which you can then style and display however you like (even alongside your existing internally-built integrations).

Why It Matters

OAuth fragmentation isn't just a developer inconvenience. It slows down feature delivery, introduces hard-to-debug issues, and increases risk. Unified.to makes OAuth something you can configure once and scale across all integrations.

You don't have to manage per-API edge cases. You get a unified abstraction for scopes, redirects, errors, token refresh, and storage.

OAuth is still OAuth under the hood. But Unified.to makes it feel like one integration.

Explore Unified.to's OAuth Docs

All articles