Vetora logo
๐Ÿ”‘Security

OAuth 2.0 & OpenID Connect

OAuth 2.0 is a delegation framework that lets users grant third-party applications limited access to their resources without sharing credentials. OpenID Connect (OIDC) adds a standardized identity layer on top, providing authentication in addition to OAuth's authorization.

Overview

OAuth 2.0, published as RFC 6749 in 2012, solves the delegated authorization problem: how can a user grant a third-party application access to their data on another service without sharing their password? Before OAuth, users routinely gave their Google or Facebook passwords to third-party apps. OAuth introduced the concept of access tokens: the user authenticates directly with the authorization server (e.g., Google), which issues a token to the third-party app. The token grants limited access (defined by scopes like 'read:email' or 'write:calendar') and can be revoked independently of the user's password.

OAuth 2.0 defines four grant types. The Authorization Code grant is the most secure: the user is redirected to the authorization server, authenticates, and the server redirects back with an authorization code. The client exchanges this code for tokens via a back-channel request, keeping tokens out of the browser's URL bar. PKCE (Proof Key for Code Exchange, RFC 7636) adds a code_verifier/code_challenge pair that prevents authorization code interception attacks, making the Authorization Code grant safe for public clients (mobile apps, SPAs). The Client Credentials grant is for machine-to-machine communication where no user is involved. The Resource Owner Password grant (deprecated) and Implicit grant (deprecated) should not be used in new systems.

OpenID Connect (OIDC) extends OAuth 2.0 with a standardized authentication layer. While OAuth 2.0 only provides an access token (authorization), OIDC adds an ID token -- a JWT containing identity claims (sub, email, name, picture). The ID token proves who the user is; the access token proves what the user has authorized. OIDC also standardizes a UserInfo endpoint and discovery metadata (.well-known/openid-configuration), enabling interoperability between identity providers.

In a modern system, the typical flow is: the SPA redirects to the authorization server (/authorize with PKCE). The user logs in (username+password, MFA, social login). The authorization server redirects back with an authorization code. The SPA exchanges the code for an ID token (identity), access token (API access), and refresh token (session continuity). The access token (short-lived, 15 minutes) is sent with API requests. When it expires, the refresh token obtains a new one without user interaction. The refresh token is long-lived (days-weeks) and stored securely (HTTP-only cookie or secure storage).

Key Points
  • 1OAuth 2.0 is an authorization framework, not an authentication protocol. It issues access tokens that grant scoped API access. It does NOT tell you who the user is -- that is what OIDC adds.
  • 2Authorization Code + PKCE is the only recommended grant type for all clients (web, mobile, SPA). The Implicit grant (tokens in URL fragment) and Resource Owner Password grant are deprecated due to security vulnerabilities.
  • 3OIDC adds ID tokens (JWTs with identity claims: sub, email, name) on top of OAuth. The ID token is consumed by the client to establish user identity. The access token is consumed by the resource server to authorize API calls.
  • 4Access tokens should be short-lived (5-15 minutes) to limit the blast radius of theft. Refresh tokens (hours-days) enable session continuity. Store refresh tokens in HTTP-only, Secure, SameSite cookies -- never in localStorage.
  • 5Scopes define what access is granted: 'openid profile email' (OIDC standard scopes), 'read:orders write:orders' (custom API scopes). The user consents to scopes during the authorization flow.
  • 6Token validation: ID tokens are validated by the client (check signature, issuer, audience, expiry). Access tokens are validated by the resource server (check signature and scopes) or via token introspection endpoint.
Simple Example

Logging into a Third-Party App with Google

You click 'Sign in with Google' on a task management app. The app redirects you to accounts.google.com with a PKCE code_challenge. You enter your Google credentials (authentication happens at Google, not the app). Google shows a consent screen: 'TaskApp wants to access your email and calendar.' You approve. Google redirects back with an authorization code. The app exchanges the code + code_verifier for: an ID token (proves you are alice@gmail.com), an access token (lets the app read your calendar), and a refresh token (maintains the session). The app never sees your Google password.

Real-World Examples

Auth0 / Okta

Auth0 (now part of Okta) is a leading identity-as-a-service platform built on OAuth 2.0 and OIDC. It provides authorization servers, social login connectors, MFA, and fine-grained scoping for thousands of organizations. Auth0's architecture handles billions of authentication requests per month, using JWKs (JSON Web Key Sets) for distributed token verification without calling back to the IdP.

GitHub

GitHub uses OAuth 2.0 for third-party app integrations. When you authorize a CI tool to access your repos, GitHub's authorization server issues a token with scopes like 'repo', 'read:org', or 'write:packages'. GitHub also supports OIDC for GitHub Actions, allowing CI jobs to authenticate to cloud providers (AWS, GCP) without storing long-lived credentials -- the OIDC token proves the job's identity.

Slack

Slack's OAuth implementation supports both user tokens (acting as a user) and bot tokens (acting as an app) with granular scopes. When you install a Slack app, the OAuth consent screen shows exactly which scopes (channels:read, chat:write, users:read) the app requests. Slack recently migrated to granular scopes from legacy umbrella scopes, reducing over-permissioning.

Trade-Offs
AspectDescription
Token Lifetime vs. SecurityShort-lived access tokens (5 minutes) minimize theft risk but require frequent refresh token exchanges, adding latency. Long-lived tokens (1 hour) reduce overhead but increase the window of exploitation if stolen. The industry standard is 15 minutes for access tokens.
Opaque vs. Self-Contained TokensOpaque tokens (random strings validated via introspection endpoint) are instantly revocable but require a network call per validation. Self-contained tokens (JWTs with embedded claims) are validated locally without network calls but cannot be revoked before expiry.
First-Party vs. Third-Party ClientsFirst-party clients (your own SPA, mobile app) can use refresh token rotation and skip the consent screen. Third-party clients must show consent screens and have restricted token lifetimes. The same OAuth server handles both but with different policies.
Complexity vs. Standards ComplianceImplementing OAuth 2.0 + OIDC correctly is complex (PKCE, token rotation, JWKS rotation, clock skew handling). Using a managed IdP (Auth0, Okta) eliminates implementation complexity but adds cost and vendor dependency.
Case Study

GitHub Actions OIDC for Keyless Cloud Authentication

Scenario

GitHub Actions CI/CD jobs needed AWS credentials to deploy infrastructure. Teams stored long-lived IAM access keys as GitHub Secrets. These keys had no expiry, were shared across all workflows in a repo, and could not be scoped to specific jobs. Key rotation was manual and infrequent. A leaked key compromised all AWS resources the key could access.

Solution

GitHub added OIDC federation for Actions. Each workflow run receives a short-lived OIDC token (JWT) signed by GitHub's IdP. The JWT contains claims like repository, branch, environment, and job ID. AWS IAM is configured to trust GitHub's IdP and exchange the OIDC token for temporary STS credentials scoped to the specific repository and branch. No long-lived credentials are stored anywhere.

Outcome

Thousands of organizations migrated from stored IAM keys to OIDC federation. Key rotation became unnecessary (tokens are per-run, lasting minutes). Blast radius was reduced: a compromised token from a PR build cannot access production resources because the branch claim does not match the IAM policy. The pattern expanded to GCP, Azure, and HashiCorp Vault.

Common Mistakes
  • โš Using the Implicit grant for SPAs. The Implicit flow returns tokens in the URL fragment, exposing them to browser history, referrer headers, and browser extensions. Use Authorization Code + PKCE instead -- it keeps tokens in back-channel responses, not URLs.
  • โš Storing tokens in localStorage. localStorage is accessible to any JavaScript on the page, making it vulnerable to XSS attacks. Store access tokens in memory (JavaScript variable) and refresh tokens in HTTP-only, Secure, SameSite=Strict cookies.
  • โš Not validating the ID token. The client must verify the ID token's signature (against the IdP's JWKS), issuer (iss matches expected), audience (aud matches client_id), and expiry (exp is in the future). Skipping any check allows token forgery or replay.
  • โš Over-scoping access tokens. Requesting 'admin' or 'full_access' scopes when 'read:profile' suffices violates the principle of least privilege. If the token is stolen, the attacker has maximum access. Always request the minimum scopes needed.
Related Concepts

See OAuth 2.0 & OpenID Connect in action

Explore system design templates that use oauth 2.0 & openid connect and run traffic simulations to see how these concepts perform under real load.

Browse Templates

Measure OAuth 2.0 token exchange overhead in checkout flow

Metrics to watch
token_exchange_latency_msrefresh_rate_per_secauth_failure_ratep99_latency_ms
Run Simulation
Test Your Understanding

1What does OpenID Connect add on top of OAuth 2.0?

2Why is Authorization Code + PKCE preferred over the Implicit grant?

Deeper Reading