System Design Guide

OAuth 2.0: Delegated Authorization Framework

OAuth 2.0 is an authorization framework enabling applications to obtain limited access to user resources without exposing credentials. Instead of sharing passwords with third-party applications, users authorize specific permissions, and applications receive access tokens for API requests. OAuth has become the standard for delegated authorization, powering “Sign in with Google/Facebook/GitHub” features and API integrations across the web.

Core Concepts

Resource Owner is the user who owns data and can grant access to it. For example, you own your Google photos and can authorize applications to access them.

Client is the application requesting access to resources. This might be a mobile app, web application, or service integrating with an API.

Authorization Server authenticates the resource owner and issues access tokens to clients after obtaining authorization. Google, Facebook, and GitHub act as authorization servers for their respective OAuth implementations.

Resource Server hosts protected resources and accepts access tokens. APIs validate tokens and return requested data if tokens grant appropriate permissions.

Access Token is a credential representing authorization to access resources. Tokens have limited lifetimes and scopes, restricting what they can access.

Scope defines what permissions an access token grants. For example, read:email might allow reading but not modifying email, while write:posts allows creating posts.

Authorization Flows

Authorization Code Flow is the most secure flow for web and mobile applications. Users authenticate with the authorization server, which redirects back to the client with an authorization code. The client exchanges this code for an access token via a secure back-channel request. This flow keeps tokens away from browsers, reducing exposure.

Implicit Flow (now deprecated) issued tokens directly in browser redirects without exchange steps. While simpler, it exposed tokens in URLs and browser history, creating security vulnerabilities. Modern applications should use Authorization Code Flow with PKCE instead.

Client Credentials Flow is for service-to-service authentication where there’s no user. The client authenticates directly with the authorization server using client credentials (ID and secret) and receives an access token. This suits backend services accessing APIs on their own behalf.

Resource Owner Password Credentials Flow (discouraged) has clients collect users’ usernames and passwords and exchange them for tokens. This requires users to trust clients with credentials, undermining OAuth’s purpose. Use only when Authorization Code Flow isn’t possible and trust is absolute.

Device Flow enables authorization on input-constrained devices like smart TVs or IoT devices. Devices display codes that users enter on other devices (phones or computers) to authorize access. This handles scenarios where typing URLs or credentials on the device is impractical.

PKCE (Proof Key for Code Exchange)

PKCE enhances security for Authorization Code Flow, particularly on mobile and single-page applications. Clients generate a random code verifier and its hashed challenge. The challenge is sent during authorization, and the verifier during token exchange. This prevents authorization code interception attacks.

PKCE should be used with all Authorization Code Flow implementations, not just mobile apps. It provides defense-in-depth without requiring client secrets, simplifying security for public clients.

Tokens

Access Tokens grant temporary access to resources. They’re bearer tokens: anyone possessing the token can use it until expiration. Keep access tokens short-lived (minutes to hours) to limit exposure if compromised.

Refresh Tokens obtain new access tokens without user interaction. While access tokens expire quickly, refresh tokens last longer (days to months). When access tokens expire, clients use refresh tokens to obtain fresh ones, maintaining long-term access without repeatedly prompting users.

ID Tokens (in OpenID Connect, an identity layer atop OAuth) convey user identity information. Unlike access tokens (authorization), ID tokens authenticate users, providing claims like name, email, and user ID.

Token Validation

Signature Verification ensures tokens haven’t been tampered with. JWTs are signed using the authorization server’s private key. Resource servers validate signatures using the public key, confirming token authenticity.

Expiration Checking verifies tokens haven’t expired. Expired tokens should be rejected, requiring clients to obtain new tokens.

Scope Validation ensures tokens grant permissions for requested operations. An access token with read:profile scope shouldn’t be accepted for write operations.

Audience Validation confirms tokens are intended for the validating resource server. Tokens issued for one API shouldn’t be accepted by unrelated APIs.

Security Considerations

Redirect URI Validation prevents authorization code injection. Authorization servers must validate redirect URIs exactly match registered URIs, preventing codes from being sent to attacker-controlled endpoints.

State Parameter protects against CSRF attacks. Clients generate random state values included in authorization requests. Upon receiving authorization responses, clients verify state matches, ensuring requests originated from their own users.

Token Storage must be secure. Never store tokens in local storage (vulnerable to XSS). Use HttpOnly, Secure cookies for web applications or secure platform storage for mobile apps.

Token Leakage through logs, URLs, or referer headers must be prevented. Tokens should never appear in URLs (use POST requests for token endpoints) and should be scrubbed from logs.

OpenID Connect

OpenID Connect extends OAuth 2.0 to provide authentication, not just authorization. While OAuth answers “what can this client access?”, OpenID Connect answers “who is this user?”.

ID Tokens contain user identity claims in JWT format. Standard claims include sub (subject identifier), name, email, and picture.

UserInfo Endpoint provides additional user information beyond ID tokens. Clients request user details using access tokens, receiving claims about the authenticated user.

Discovery allows automatic configuration. OpenID Connect providers expose discovery documents at /.well-known/openid-configuration describing endpoints, supported features, and keys for token validation.

Common Mistakes

Using Implicit Flow exposes tokens unnecessarily. Use Authorization Code Flow with PKCE instead, even for single-page applications.

Long-Lived Access Tokens increase compromise risk. Keep access tokens short-lived and use refresh tokens for long-term access.

Insufficient Redirect URI Validation enables authorization code injection. Validate redirect URIs exactly, not with substring or prefix matching.

Storing Secrets Client-Side defeats security. Public clients (mobile, single-page apps) can’t keep secrets; use PKCE instead of client secrets.

Ignoring Security Advice from OAuth specifications and experts invites vulnerabilities. Follow best practices and security guidelines carefully.

Implementation Guidance

Use Proven Libraries rather than implementing OAuth from scratch. Libraries like Spring Security OAuth, Passport.js, or Auth0 handle complex details correctly.

Register Redirect URIs Carefully. For development, use localhost or private addresses. For production, use HTTPS URIs. Never use wildcard or overly permissive patterns.

Implement Token Refresh Properly. Attempt refresh when access tokens expire, handle refresh token expiration by redirecting to login, and implement proper error handling.

Secure Token Storage. Use platform-specific secure storage: HttpOnly cookies for web, Keychain/Keystore for mobile, encrypted storage for desktop applications.

Monitor Token Usage. Track token issuance, refresh patterns, and unusual activity. Anomalies might indicate compromised tokens or credential stuffing attacks.

Token Revocation

Revocation Endpoints allow clients to invalidate tokens before expiration. When users log out or revoke application permissions, clients should revoke tokens.

Revocation Lists track revoked tokens. While JWTs are stateless, maintaining revocation lists ensures compromised tokens can be invalidated. Balance between security (checking every token) and performance (caching validation).

Token Expiration is the simplest revocation: short-lived tokens automatically become invalid quickly, limiting damage from compromised tokens.

Best Practices

Always use HTTPS for OAuth flows. Tokens and authorization codes must be protected in transit.

Implement proper error handling. OAuth flows have many error conditions; handle them gracefully without exposing sensitive details.

Validate all tokens thoroughly: signatures, expiration, audience, and scopes. Each validation step catches different attack classes.

Use appropriate flows for client types. Authorization Code Flow with PKCE for web and mobile, Client Credentials for services, Device Flow for input-constrained devices.

Educate users about permissions. Make clear what access they’re granting. Users should understand authorization before approving.

Monitor and audit OAuth usage. Track authorization grants, token usage patterns, and revocations. Alert on anomalies.

Keep OAuth libraries and dependencies updated. Security vulnerabilities in OAuth implementations are discovered periodically; staying current is essential.

OAuth 2.0 provides powerful mechanisms for delegated authorization, enabling secure third-party access without sharing credentials. Understanding OAuth flows, security considerations, and best practices enables implementing authorization securely while providing good user experiences. While OAuth has complexity, its benefits—eliminating password sharing, limiting access scope, and enabling token revocation—make it the standard choice for API authorization and federated authentication. The key is implementing it correctly, following specifications and security guidance to avoid common pitfalls.