In this article
July 27, 2026
July 27, 2026

The real cost of building one OAuth integration

What 150 hours of engineering actually buys you, and what it doesn't.

Explore with AI
Open in ChatGPT
Open in Claude
Open in Perplexity

Most teams underestimate this by a factor of ten.

The conversation usually goes something like this: a product manager asks how long it would take to add a Slack integration. A developer thinks about the OAuth flow, maybe a day or two to get the tokens, another day to hook up the API calls. They say a week to be safe. The PM puts it on the roadmap. Four weeks later the integration ships, slightly broken, and a senior engineer is now part-owner of an OAuth system they didn't plan to maintain indefinitely.

This isn't a failure of estimation. It's a failure of scope. What most people imagine when they think about "building an OAuth integration" is the authorization flow: the redirect, the code exchange, the token. That part is roughly as fast as they expect. What they don't account for is everything that has to exist around it for the integration to actually work in production for real users over time.

This article is an honest accounting of what that is.

What you actually have to build

A working OAuth integration in production isn't a single thing. It's a collection of components that have to be designed, built, tested, and maintained together. Here's what that list looks like for a single provider.

  • Client credential management. Before writing a line of code, you register an OAuth application with the provider, get a client ID and client secret, and store them safely. Safely means environment variables at minimum, a secrets manager ideally, and never in source control. For providers like Google, you also configure which scopes your app is allowed to request, which requires knowing in advance what your integration will need. This setup is fast but consequential: misconfiguring redirect URIs here will cause silent failures in production.
  • The authorization flow. This is the part people picture: building a URL with the right query parameters, redirecting the user, handling the callback. It sounds simple, and the first version is. The complications start immediately. The state parameter, which most implementations skip in early versions, is your main defense against CSRF attacks: you generate a random value, tie it to the session, include it in the redirect, verify it matches when the callback arrives. Skip it and you have a security vulnerability. Different providers also handle scope formatting differently (spaces, commas, URL-encoded strings), require different additional parameters, and behave differently when the user denies consent.
  • Token storage. After the code exchange you have an access token and usually a refresh token. These need to be stored somewhere. "Stored somewhere" is doing a lot of work in that sentence. Tokens are effectively API keys for your users' data in third-party systems. Storing them in plaintext in your database is a genuine security failure, not just a best-practice miss. You need field-level encryption: encrypt before insert, decrypt after retrieval. That requires a key management system and a clear plan for key rotation. The often-skipped detail: encrypting tokens in a way that still allows you to find the right token for a given user efficiently, with proper database indexing on the non-sensitive fields. This is a real schema design problem.
  • Token refresh. Access tokens expire. Google's expire after an hour. Slack's expire on a schedule. GitHub's vary depending on the token type. When a token expires, you need a fresh one, which means using the refresh token to get it. This sounds like a one-line API call and is actually one of the hardest parts of the whole system to get right in production. The naive implementation checks whether a token is expired when you need it, refreshes it if so, then retries. This breaks at scale because multiple concurrent requests for the same user can all see the expired token simultaneously, all attempt to refresh it at once, and some of those refreshes will use an already-invalidated refresh token, leaving you with no valid token for that user and no clean way to recover. The production solution requires distributed locking: before refreshing, acquire a lock on that user-provider pair, check again inside the lock whether the token is still expired (another worker may have already refreshed it), refresh if needed, release the lock. This requires Redis or an equivalent and careful lock TTL management. It's a distributed systems problem wrapped in what looked like a one-liner.
  • Token revocation and reconnection. Users revoke access. They disconnect your app from their Slack settings, or their company admin revokes it, or their Google account password changes and invalidates all outstanding tokens. When this happens, your next API call will fail. You need to detect that failure, distinguish it from a normal API error or a transient network issue, update the token status in your database, and surface a reconnect prompt in your UI. Each provider returns different error codes for different revocation scenarios. Microsoft, for example, uses overlapping error codes to represent several different conditions: expired token, user disabled, and password changed can all come back as invalid_grant, with the actual cause buried in a sub-error code. You build a lookup table for each provider, which you maintain as providers change their error formats.
  • The settings UI. Users need a place to connect and disconnect their accounts. This is a UI you build and maintain. It needs to show connection status, handle the reconnect flow gracefully when a token has expired, and give users confidence that your app only accesses what they authorized. This UI doesn't write itself, and it's not just cosmetic: a confusing connection UI increases support tickets and reduces authorization rates.
  • Scope management. Scopes change. Google periodically deprecates scopes and requires re-verification for new ones. If your integration needs additional permissions you didn't originally request, you need to handle incremental authorization: ask for new scopes without forcing users to re-authorize everything. Some providers support this cleanly. Others require you to re-do the full flow. If you add a sensitive scope (Google classifies Calendar, Gmail, and Drive as sensitive), you need to go through an OAuth verification process before you can use it in production with arbitrary users. That process involves a security review, a privacy policy assessment, and sometimes a third-party audit. It takes weeks.

The numbers

Based on industry estimates from teams that have done this, here is roughly what each component costs in senior engineering hours for a single provider:

Component Hours
App registration, credential management, redirect URI setup 4
Authorization flow, state/CSRF handling, callback endpoint 16
Token storage with field-level encryption 24
Token refresh with distributed locking 32
Revocation detection and reconnect flow 20
Settings UI (connect/disconnect/reconnect states) 24
Scope management and incremental authorization 16
Error handling, logging, provider-specific quirks 14
Total ~150 hours

150 hours is roughly a month of a senior engineer's time, fully allocated, for one provider. At a fully-loaded engineering cost of $150,000 per year (salary plus benefits and overhead), that's around $10,800 in direct labor to build the integration.

Building is only the beginning. The same sources estimate around 300 hours per year in ongoing maintenance per integration: handling provider API changes, debugging edge cases in token refresh, updating scope configurations, responding to user-reported connection failures. That's another $21,600 per year.

Customer success overhead adds more. A reasonable estimate is 150 support tickets per year related to a single integration, at around two hours of CSM time per ticket. At a CSM salary of $110,000 (about $53 per hour), that's $15,900 per year in support cost.

Total first-year cost for one OAuth integration, across engineering build, engineering maintenance, and customer success: approximately $48,000. Per integration. Per year.

Most product teams have between three and ten integrations on their roadmap.

The parts that are hard to budget for

The cost estimate above is for a well-run implementation where things go roughly as planned. The following are the scenarios that don't appear in sprint planning and are where a lot of the real cost lives.

  • Provider API changes. Google, Slack, Microsoft, and GitHub regularly change how their OAuth flows work: deprecating scopes, modifying refresh token behavior, changing error response formats, updating redirect URI validation rules. These changes arrive via changelog emails that engineers may or may not see, or via a burst of production errors that arrive first. Each one requires investigation, a code change, testing, and a deploy. There is no fixed schedule for this. It happens when it happens.
  • The 7-day testing mode trap. Google only issues long-lived refresh tokens to apps in production status that have completed OAuth verification for sensitive scopes. While your app is in testing mode, refresh tokens expire after 7 days. This means a Calendar or Gmail integration that works perfectly in development starts breaking for real users about a week after they connect. This is a common source of confused bug reports that say "it worked last week." The fix (completing Google's verification process) takes weeks and involves paperwork.
  • Refresh token inactivity expiry. Most teams know access tokens expire. Fewer plan for refresh token expiry. Google refresh tokens expire after 6 months of inactivity. Microsoft's expire after 90 days by default, and also expire when the user changes their password. GitHub's fine-grained tokens expire after 6 months. A background job that runs nightly keeps most tokens fresh, but what about an enterprise customer that uses the feature quarterly? Their refresh token expired two months ago. The next time the agent tries to run, it fails, and your error handling has to correctly identify this as "needs re-authorization" rather than "API is down."
  • The re-verification trap. If you need to add a new scope to an existing Google integration (say, you've added Gmail support to an app that previously only used Calendar), you need to re-verify your OAuth application. Users who authorized the old scope bundle don't automatically see the new consent screen. You have to force re-consent with prompt=consent, which re-prompts every user, or request the new scope incrementally. Either way, you're touching production auth flows for existing users, which is high-risk. And re-verification takes weeks.
  • Key rotation. At some point your security team will rotate your encryption keys. Now you need to decrypt every stored token (with the old key) and re-encrypt it (with the new key), atomically, without downtime, for potentially thousands of rows. If this migration fails partway through, some users' tokens can no longer be decrypted and their integrations silently break. This is an infrastructure event that requires careful planning every time it happens.

What this looks like when multiplied

The cost and complexity above is for one provider. The economics change when you have multiple integrations.

Some of the work is fixed: the UI patterns for connecting and disconnecting accounts, the token storage schema, the distributed lock infrastructure. These don't scale linearly with the number of providers. But a significant portion does: each provider has its own quirks, its own error codes, its own scope format, its own refresh token behavior, its own edge cases. Every new provider means a new surface area to monitor and maintain.

A team with five integrations doesn't have five times the work of a team with one. But they have substantially more than twice as much. And they've accumulated a system that, unlike their core product, generates no direct revenue and grows more complex over time regardless of how carefully it's managed.

The pattern that avoids this

The problem isn't OAuth itself. OAuth is the right protocol for this. The problem is that each team building integrations is individually solving the same infrastructure problems: token storage, refresh logic, revocation handling, CSRF protection, scope management. None of these solutions compound. Each one is built once and maintained forever by whoever inherited it.

WorkOS Pipes is built on the observation that this infrastructure is the same across providers and across products. The OAuth flow, the token lifecycle, the reconnect UI, the encryption layer: these are solved problems that don't need to be solved again in every codebase.

With Pipes, you configure a provider once in the WorkOS dashboard. Your users connect their accounts through the Pipes widget, which handles the authorization flow, the consent screen, and the settings UI. When your backend needs to call the provider's API, you make one call to get a fresh, valid access token:

  
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS(process.env.WORKOS_API_KEY);

const { accessToken } = await workos.pipes.getAccessToken({
  provider: 'slack',
  userId: userId,
});

// Use accessToken to call the Slack API directly
  

The token storage, the refresh logic, the distributed locking, the revocation detection, the re-authorization prompts: all of that is handled for you. If the token is expired, Pipes refreshes it. If it's been revoked, the error you receive tells you what happened and what to do about it. You don't write the retry logic or the error classification or the reconnect flow.

The 150 hours becomes something closer to an afternoon.

The opportunity cost framing matters here too. A senior engineer spending a month on an OAuth integration is a senior engineer not spending a month on your core product. For most teams, the integrations page is not the product. It's infrastructure that enables the product. Treating it as infrastructure, and managing it as infrastructure, is how the teams that ship the most actually ship the most.

WorkOS Pipes documentation is at workos.com/docs/pipes. Shared credentials are available for development, so you can prototype any integration without registering an OAuth application first.