How OAuth 2.0 Works: Delegated Authorization Explained
Every time you click "Sign in with Google" or let a third-party app access your GitHub repositories, you are using OAuth 2.0. It is the authorization framework that lets you grant limited access to your accounts on one service to another service — without ever sharing your password.
OAuth 2.0 is defined in RFC 6749 (2012) and has become the de facto standard for delegated authorization on the web. Understanding how it works is essential for anyone building or securing web applications, APIs, or mobile apps.
The Problem: Why Not Just Share Passwords?
Before OAuth, the way to let a third-party application access your data was to give it your username and password. If you wanted a printing service to access your photos stored on another site, you would hand over your credentials. This had severe problems:
- Full access — The third-party app gets unrestricted access to your account. You cannot limit it to just reading photos; it can do anything you can do.
- Password exposure — Your credentials are stored by a third party. If they are breached, your account is compromised.
- No revocation — The only way to revoke access is to change your password, which breaks every other application you gave your credentials to.
- Trust escalation — You are trusting the third-party app to handle your credentials securely, which is a much bigger ask than trusting it with limited data access.
OAuth solves this by introducing an authorization layer that separates the role of the client (the app that wants access) from the resource owner (you). Instead of sharing credentials, the resource owner grants the client a token with specific, limited permissions.
The Four Roles
OAuth 2.0 defines four distinct roles that interact during an authorization flow:
- Resource Owner — The user who owns the data and can grant access to it. This is you when you click "Allow" on a consent screen.
- Client — The application requesting access to the resource owner's data. This could be a web app, mobile app, or CLI tool.
- Authorization Server — The server that authenticates the resource owner and issues access tokens. For "Sign in with Google," this is Google's authorization server at
accounts.google.com. - Resource Server — The server hosting the protected resources (APIs). It accepts access tokens and serves data. This might be Google's API server at
www.googleapis.com.
In practice, the authorization server and resource server are often operated by the same organization (Google, GitHub, etc.), but they are logically separate components with different responsibilities.
The Authorization Code Flow
The Authorization Code grant is the most common and most secure OAuth 2.0 flow. It is used by web applications and mobile apps to obtain access tokens. Here is how it works, step by step:
Let us walk through each step:
- The user clicks "Sign in" on the client application. The client constructs an authorization URL pointing to the authorization server.
- The client redirects the user's browser to the authorization server. The redirect URL includes the client ID, requested scopes, a redirect URI, and a random
stateparameter (for CSRF protection). - The user authenticates directly with the authorization server (enters their Google password, for example) and sees a consent screen listing the permissions the client is requesting. The user's password is never exposed to the client application.
- The authorization server redirects back to the client's redirect URI with a short-lived authorization code in the URL query string.
- The browser follows the redirect, delivering the authorization code to the client's backend server.
- The client's server exchanges the authorization code for tokens by making a direct server-to-server POST request to the authorization server's token endpoint. This request includes the client's secret, which proves the client's identity.
- The authorization server returns an access token (and optionally a refresh token). The authorization code is now consumed and cannot be reused.
- The client uses the access token to make API requests to the resource server, typically in the
Authorization: Bearer <token>header. - The resource server validates the token and returns the requested data.
The key security insight: the authorization code is exchanged via the user's browser (the "front channel"), but the token exchange happens directly between the client server and the authorization server (the "back channel"). The access token never passes through the browser. This entire flow runs over TLS/HTTPS, so all data in transit is encrypted.
Tokens: Access Tokens and Refresh Tokens
OAuth 2.0 uses two types of tokens:
Access tokens are credentials that the client presents to the resource server to access protected resources. They are typically short-lived (minutes to hours). An access token can be an opaque string (a random identifier that the resource server looks up in a database) or a self-contained token like a JSON Web Token (JWT) that carries its own claims and can be verified without a database lookup.
Refresh tokens are long-lived credentials used to obtain new access tokens after the current one expires. Unlike access tokens, refresh tokens are only ever sent to the authorization server — never to the resource server. This limits their exposure. If an access token is leaked, the damage is time-limited. If a refresh token is leaked, the authorization server can revoke it.
Scopes: Limiting What Access Tokens Can Do
Scopes define the boundaries of access that the client is requesting. When the client redirects the user to the authorization server, it includes a scope parameter listing the permissions it needs. The user sees these scopes on the consent screen and can choose to grant or deny them.
Scopes are strings defined by the resource server. There is no universal standard for scope naming, but some common patterns exist:
read:user— Read the user's profile informationrepo— Full access to repositories (GitHub)email— Access the user's email addressopenid profile— OpenID Connect scopes for identityhttps://www.googleapis.com/auth/drive.readonly— Read-only access to Google Drive
The principle of least privilege applies: clients should request only the scopes they need. A photo printing service needs read access to your photos, not write access to your email. Users have learned to be suspicious of apps requesting broad scopes.
Grant Types
OAuth 2.0 defines several grant types (also called flows) for different use cases. Each represents a different way for the client to obtain an access token.
Authorization Code with PKCE
PKCE (Proof Key for Code Exchange, pronounced "pixy," defined in RFC 7636) is an extension to the authorization code flow designed to protect against authorization code interception attacks. It is now recommended for all OAuth clients — not just mobile apps where it originated.
With PKCE, the client generates a random code_verifier and derives a code_challenge from it (typically using SHA-256). The code challenge is sent in the initial authorization request, and the code verifier is sent when exchanging the authorization code for tokens. The authorization server verifies that they match, proving that the client that initiated the flow is the same one completing it.
This defeats attacks where a malicious app intercepts the authorization code (via a custom URL scheme on mobile, for example) but cannot complete the exchange because it does not know the code verifier.
Client Credentials
The client credentials grant is for machine-to-machine communication where no user is involved. The client authenticates directly with the authorization server using its client ID and client secret, and receives an access token. There is no browser redirect, no user consent, and no authorization code.
This is used for backend services that need to access APIs on their own behalf — for example, a cron job that reads analytics data, or a microservice calling another microservice.
Device Code
The device code grant (RFC 8628) is designed for devices that lack a browser or have limited input capabilities — smart TVs, game consoles, CLI tools, and IoT devices. The device displays a URL and a short user code. The user visits the URL on their phone or computer, enters the code, and authorizes the device. Meanwhile, the device polls the authorization server until authorization is granted.
You see this flow when logging into streaming apps on a smart TV: the TV shows "Visit example.com/activate and enter code ABCD-EFGH."
OpenID Connect: Adding Authentication
OAuth 2.0 is an authorization framework — it answers "what is this app allowed to do?" but does not inherently answer "who is this user?" OpenID Connect (OIDC) is a thin identity layer built on top of OAuth 2.0 that adds authentication — it tells the client who the user is.
The key difference: OAuth alone gives you an access token to call APIs. OpenID Connect additionally gives you an ID token — a JWT that contains claims about the authenticated user (their name, email, unique identifier, etc.). The ID token is signed by the authorization server (called the OpenID Provider in OIDC terminology) and can be verified by the client.
To use OpenID Connect, the client includes the openid scope in the authorization request. The authorization server then returns an ID token alongside the access token. Common additional scopes include profile (name, picture) and email.
"Sign in with Google," "Sign in with Apple," and "Log in with GitHub" all use OpenID Connect. When you see "This app wants to access your name and email," that is an OIDC consent screen. The application receives an ID token confirming your identity and (optionally) an access token to call Google/Apple/GitHub APIs on your behalf.
Token Introspection and Revocation
Token introspection (RFC 7662) allows a resource server to query the authorization server about a token: "Is this token valid? What scopes does it have? When does it expire? Who is it for?" This is necessary when tokens are opaque strings — the resource server cannot decode them locally and must ask the authorization server. Self-contained JWTs can be validated locally without introspection, but introspection can still be used to check revocation status.
Token revocation (RFC 7009) allows a client to notify the authorization server that a token is no longer needed and should be invalidated immediately. This is what happens when you click "Sign out" — the client revokes the access and refresh tokens so they cannot be used even if intercepted. Revocation is especially important for refresh tokens, which are long-lived.
The Token Request in Detail
The token exchange (step 6 in the authorization code flow) is a standard HTTP POST request. Here is what it looks like:
POST /oauth/token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https://app.example.com/callback
&client_id=s6BhdRkqt3
&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
The authorization server validates the code, verifies the client's identity (via the secret or PKCE verifier), and returns a JSON response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
"scope": "read:user email",
"id_token": "eyJhbGciOiJSUzI1NiIs..."
}
The access_token and id_token here are JWTs — base64url-encoded JSON payloads with a cryptographic signature. The access token is presented to resource servers in the Authorization header. The ID token is consumed by the client to learn who the user is.
Security Best Practices
OAuth 2.0 is a framework with many moving parts. Implementing it incorrectly creates vulnerabilities. These are the critical security practices:
- Always use PKCE — Even for confidential clients with a client secret. PKCE protects against authorization code interception. The OAuth 2.1 draft makes PKCE mandatory for all clients.
- Use the
stateparameter — Generate a random, unguessablestatevalue and include it in the authorization request. Verify it matches when the callback arrives. This prevents cross-site request forgery (CSRF) attacks where an attacker tricks a user into authorizing the attacker's account. - Validate redirect URIs strictly — The authorization server must use exact-match comparison for redirect URIs. Open redirectors allow authorization codes to be intercepted. Never allow wildcard or partial redirect URI matching.
- Keep tokens out of URLs — Access tokens in URL query strings are logged by browsers, proxies, and servers. Use the authorization code flow (where only the short-lived, one-time code appears in the URL) rather than the deprecated implicit flow.
- Use short-lived access tokens — Limit access token lifetimes to minutes or a few hours. Combined with refresh tokens, this limits the damage from token theft.
- Rotate refresh tokens — Issue a new refresh token each time one is used, and invalidate the old one. If a stolen refresh token is used, the legitimate client's next refresh will fail, revealing the compromise.
- Store tokens securely — On the server side, treat refresh tokens like passwords. On the client side in browsers, use secure HTTP-only cookies — never store tokens in
localStorage, which is accessible to any JavaScript running on the page (XSS vulnerability). - Always use HTTPS/TLS — OAuth 2.0 relies entirely on transport-layer security. Without TLS, tokens and authorization codes can be intercepted in transit. OAuth 2.0 explicitly requires HTTPS for all endpoints.
- Validate ID tokens — When using OpenID Connect, verify the JWT signature, check the issuer (
iss) and audience (aud) claims, and confirm the token is not expired. Never trust an ID token without validation.
Real-World Example: Sign in with Google
When you click "Sign in with Google" on a website, here is exactly what happens:
- The website redirects you to
https://accounts.google.com/o/oauth2/v2/authwith parameters specifying the client ID (registered in Google Cloud Console), requested scopes (openid email profile), and a redirect URI back to the website. - Google shows you a login screen (if not already signed in) and a consent screen listing what the website wants to access.
- You click "Allow." Google redirects your browser back to the website's redirect URI with an authorization code.
- The website's server exchanges the code at
https://oauth2.googleapis.com/tokenusing its client ID and client secret. - Google returns an access token, refresh token, and ID token (a JWT containing your Google user ID, name, email, and profile picture).
- The website reads the ID token to learn who you are, creates or updates your account, and sets a session cookie.
- If the website needs ongoing access to Google APIs (like reading your Google Calendar), it stores the refresh token and uses it to get new access tokens as needed.
The entire authentication flow happens over HTTPS, your Google password is never exposed to the website, and you can revoke the website's access at any time from your Google Account settings without changing your password.
OAuth 2.0 and Network Infrastructure
While OAuth is an application-layer protocol, it depends heavily on the network infrastructure covered throughout this site. Every OAuth flow requires reliable, secure network connectivity:
- DNS resolves the authorization and token endpoints. DNS hijacking could redirect users to a fake authorization server — which is why DNS over HTTPS and DNSSEC add important layers of protection.
- TLS encrypts all OAuth traffic. Without TLS, the entire protocol is insecure. Certificate validation ensures the client is talking to the legitimate authorization server.
- BGP routes packets between the client, user, authorization server, and resource server. A BGP hijack could theoretically redirect traffic to an attacker's server, though TLS certificate validation would cause the connection to fail (the attacker cannot produce a valid certificate for the legitimate domain).
- CDNs and anycast help authorization servers like Google's and Microsoft's handle billions of token requests with low latency worldwide.
This is defense in depth: OAuth provides application-layer authorization, TLS provides transport-layer encryption, RPKI provides routing-layer verification, and each layer protects against failures in the others.
See It in Action
OAuth 2.0 authorization servers run on infrastructure routed through BGP like everything else on the internet. You can explore the networks that power major identity providers:
- AS15169 — Google (powers "Sign in with Google")
- AS8075 — Microsoft (powers Azure AD / Entra ID / "Sign in with Microsoft")
- AS36459 — GitHub (powers "Sign in with GitHub")
- AS714 — Apple (powers "Sign in with Apple")
- AS32934 — Meta/Facebook (powers Facebook Login)
- accounts.google.com — Resolve Google's authorization server