OAuth: Delegating Trust
The authorization framework that lets you 'Sign in with Google' solved the password-sharing problem and enabled the platform economy.
TL;DR
In the mid-2000s, web applications wanted to talk to each other. A photo printing service needed access to your Flickr photos. A social app needed to post to your Twitter. The only way to do this was to give the third-party app your password — and trust that they wouldn’t abuse it, store it insecurely, or use it for anything you didn’t authorize. In 2007, Blaine Cook (Twitter) and Chris Messina proposed an open standard for delegated authorization: a way for users to grant limited access to their data without sharing credentials. The result was OAuth — a protocol where the user authenticates directly with the service they trust, and that service issues a scoped, revocable token to the third-party app. OAuth didn’t authenticate users (that came later with OpenID Connect). It authorized applications. That distinction — and the token-based architecture it introduced — became the foundation of “Sign in with Google,” the API economy, and the platform model that defines the modern web.
The Password Anti-Pattern
In 2006, the web was full of services that wanted access to other services on your behalf.
A photo printing service wanted to access your Flickr album. A contact importer wanted to read your Gmail address book. A social app wanted to post to your MySpace page. Every one of them had the same request: give us your username and password.
"To import your Gmail contacts, enter your Gmail password below:"
┌─────────────────────────────┐
│ Email: alice@gmail.com │
│ Password: •••••••• │
│ │
│ [ Import ] │
└─────────────────────────────┘
This was the actual state of the art. Third-party apps collected your passwords, stored them (often in plaintext), and logged in as you. The problems were severe:
- No scoping — the app had full access to your account, not just the data you intended to share
- No revocation — to cut off access, you had to change your password, which broke every other app using it
- No trust boundary — a compromised third-party app meant your primary account was compromised
- No audit trail — you couldn’t see what the app had done with your credentials
The industry needed a way to delegate access without delegating identity. A way to say “this app can read my photos” without saying “this app can be me.”
The Protocol
OAuth was designed by Blaine Cook and Chris Messina in 2007, driven by the practical needs of Twitter’s emerging API ecosystem. The core idea: the user never gives their credentials to the third-party app. Instead, they authenticate directly with the service they trust, and that service issues a token — a limited, revocable key that the third-party app can use.
The OAuth 2.0 Authorization Code flow — the most common pattern — works like this:
User Third-Party App Authorization Server
│ │ (Google, GitHub, etc.)
│ "Connect my │ │
│ account" │ │
│ ───────────────> │ │
│ │ │
│ redirect to auth server │
│ <─────────────── │ │
│ │
│ "App X wants to read your photos. │
│ Allow / Deny?" │
│ ────────────────────────────────────────> │
│ │
│ "Allow" │
│ ────────────────────────────────────────> │
│ │
│ redirect back with authorization code │
│ <──────────────────────────────────────── │
│ │ │
│ code │ exchange code for │
│ ───────────────> │ access token │
│ │ ─────────────────────> │
│ │ │
│ │ access_token + │
│ │ refresh_token │
│ │ <───────────────────── │
│ │ │
│ │ GET /photos │
│ │ Authorization: │
│ │ Bearer <token> │
│ │ ─────────────────────> │
The user’s password never leaves the authorization server. The third-party app receives a token, not credentials. The token is scoped (can only do specific things), time-limited (expires), and revocable (the user can cut off access without changing their password).
Tokens, Not Sessions
OAuth replaced password sharing with tokens — opaque strings that represent a specific grant of access:
# The token in practice
GET /api/user/photos HTTP/1.1
Host: api.flickr.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
An access token is short-lived (minutes to hours) and used for API requests. A refresh token is long-lived and used to get new access tokens when the current one expires. This separation limits the damage if an access token is stolen — it expires quickly and can’t be used to get more tokens.
Scopes define what the token can do:
# Requesting specific permissions
GET /authorize?
client_id=app_123&
scope=read:photos+read:profile&
redirect_uri=https://printapp.com/callback&
response_type=code
The user sees exactly what they’re granting: “PrintApp wants to read your photos and profile. Allow?” Not full account access. Not your password. Just the specific permissions the app needs.
# Server-side: exchanging the authorization code for tokens
import requests
response = requests.post("https://auth.example.com/token", data={
"grant_type": "authorization_code",
"code": "abc123", # from the redirect
"client_id": "app_123",
"client_secret": "secret_456", # proves the app's identity
"redirect_uri": "https://printapp.com/callback",
})
tokens = response.json()
# {
# "access_token": "eyJhbG...",
# "token_type": "Bearer",
# "expires_in": 3600,
# "refresh_token": "dGhpcyBpcyBh..."
# }
OpenID Connect: Authentication on Top
OAuth 2.0 solves authorization — “what can this app do?” — but not authentication — “who is this user?” The original spec deliberately avoided identity. But the market wanted “Sign in with Google,” not just “let this app access my Google data.”
OpenID Connect (OIDC), published in 2014, added an identity layer on top of OAuth 2.0. When a user authenticates, the authorization server returns an ID token — a JSON Web Token containing the user’s identity claims:
{
"iss": "https://accounts.google.com",
"sub": "user_42",
"email": "alice@gmail.com",
"name": "Alice",
"picture": "https://...",
"iat": 1700000000,
"exp": 1700003600
}
The ID token is signed by the authorization server, so the app can verify it without making another request. This is the mechanism behind every “Sign in with Google / GitHub / Apple” button on the web:
1. User clicks "Sign in with Google"
2. App redirects to Google's auth page
3. User authenticates with Google
4. Google redirects back with an authorization code
5. App exchanges code for access_token + id_token
6. App reads id_token to learn the user's identity
7. App creates a local session — user is logged in
The user never creates a new password. The app never stores credentials. Google handles authentication. The app handles authorization. Each system does what it’s good at.
The Platform Economy
OAuth didn’t just solve a security problem. It enabled a business model.
Before OAuth, APIs were either open (no authentication, limited utility) or required shared secrets (fragile, insecure). OAuth gave platforms a way to offer structured, controlled access to their data:
Facebook Platform (2007) — let third-party apps access social graph data with user consent. FarmVille, Spotify social features, and the entire Facebook app ecosystem ran on OAuth tokens.
GitHub API — developers authorize CI/CD tools, project management apps, and code analysis services to access their repositories. The token scopes (repo, read:org, write:packages) precisely control what each integration can do.
Google APIs — Gmail, Calendar, Drive, Maps — hundreds of services, all accessible through OAuth tokens with granular scopes. The entire Google Cloud ecosystem uses OAuth for service-to-service authentication.
The pattern is: a platform exposes an API, OAuth controls who can access what, and an ecosystem of third-party apps grows on top. The platform gets distribution and lock-in. The apps get data and users. The user gets a “Sign in with” button instead of yet another password. OAuth is the trust protocol that makes this triangle work.
What OAuth Got Right
OAuth is complex, frequently misimplemented, and widely criticized — and it’s the authorization standard for the entire web:
- Tokens over passwords — the fundamental insight that you can grant access without sharing credentials created a new trust model for the web. The token is scoped, time-limited, and revocable. The password is none of those things. Every modern API uses bearer tokens because OAuth established the pattern.
- Separation of authentication and authorization — by keeping “who are you?” and “what can you do?” as separate concerns, OAuth enabled different solutions for each. OpenID Connect handles identity. OAuth handles permissions. This separation is clean and composable — the same authorization server can support multiple identity providers, and the same identity provider can support multiple authorization models.
- Scoped, revocable access — the ability to grant an app read-only access to your photos (and nothing else), and revoke that access at any time (without changing your password), was a genuine advance in user agency. The OAuth consent screen — “App X wants to access Y. Allow?” — is one of the few places where users actually see and control what third parties can do.
- The redirect-based flow — having the user authenticate on the authorization server’s own page (not in the third-party app) means the app never handles credentials. This is the key security property: the trust boundary is clear, the credentials stay with the trusted party, and the third-party app only ever sees tokens.
OAuth solved the password-sharing problem that plagued the early API economy. It’s overengineered for simple cases, underspecified for complex ones, and the source of an entire industry of identity-as-a-service providers. But the core model — authenticate with someone you trust, authorize with scoped tokens, revoke without changing your password — is the right architecture. Every “Sign in with” button, every API key, every third-party integration on the modern web runs on OAuth’s ideas.