Last updated: April 27, 2025
Table of Contents
1. Introduction: What Problem Does OAuth 2.0 Solve?
Imagine you want a third-party ("GameTracker") to access your game library stored on another service ("Steam") to print them. You wouldn't want to give GameTracker your Steam username and password, as that grants them full access to everything! This is the problem OAuth 2.0 solves.
OAuth 2.0 is an industry-standard authorization framework, not an authentication protocol. It enables a third-party application (like GameTracker) to obtain limited, delegated access to a user's resources on another service (like Steam), without exposing the user's credentials. Think of it like giving a valet a key that only starts the car and opens the driver door, but doesn't open the trunk or glove compartment – it grants specific, limited permissions.
2. Core Concepts and Roles
Understanding OAuth 2.0 involves knowing the key players:
- Resource Owner: The user who owns the data/resources and can grant access. (You, in the game library example)
- Client Application: The third-party application requesting access to the Resource Owner's resources. (GameTracker app)
- Authorization Server: The server that authenticates the Resource Owner, receives their consent, and issues Access Tokens to the Client Application. (Steam' authorization service)
- Resource Server: The server hosting the protected resources (e.g., APIs, user data) that accepts and validates Access Tokens from the Client Application. (Steam' API server hosting your games)
Often, the Authorization Server and Resource Server are part of the same logical service (like Steam in the example).
3. OAuth 2.0 Grant Types (Flows)
OAuth 2.0 defines several ways (called "grant types" or "flows") for a Client Application to obtain an Access Token. The appropriate flow depends on the type of application (web server app, single-page app (SPA), native mobile app, machine-to-machine service) and the level of trust.
3.1 Authorization Code Grant (with PKCE)
This is the most common and recommended flow for web applications, native apps, and SPAs where a user is involved. It's considered the most secure standard flow.
High-level steps:
- The Client Application redirects the Resource Owner (user) to the Authorization Server's
/authorize
endpoint, specifying the requested permissions (scopes), its client ID, a redirect URI, and importantly for modern security, PKCE parameters (code_challenge
,code_challenge_method
). - The Authorization Server authenticates the Resource Owner (e.g., via login form) and prompts them to grant or deny the Client's requested permissions.
- If the user grants permission, the Authorization Server redirects the user back to the Client Application's pre-registered
redirect_uri
with a temporary, single-use Authorization Code. - The Client Application (typically its backend server) exchanges this Authorization Code, along with its client credentials (client ID and secret, if applicable) and the PKCE
code_verifier
, directly with the Authorization Server's/token
endpoint. - The Authorization Server verifies the code, client credentials, and the PKCE verifier. If valid, it issues an Access Token (and usually a Refresh Token) back to the Client Application.
PKCE (Proof Key for Code Exchange) is crucial for public clients (like SPAs and native apps) that cannot securely store a client secret. It involves the client creating a secret (code_verifier
), sending a transformed version (code_challenge
) in the initial request, and proving possession of the original secret when exchanging the code. This prevents attackers who might intercept the authorization code from exchanging it for a token.
3.1.1 Example Flow
Let's illustrate with simplified URLs and parameters:
- Client Initiates Request: The client app generates a random
state
value and a PKCEcode_verifier
. It derives thecode_challenge
(e.g., SHA256 hash of the verifier, Base64URL encoded). It redirects the user's browser to the Authorization Server:https://auth.example.com/authorize? response_type=code& client_id=YOUR_CLIENT_ID& redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback& scope=read_profile%20read_game& state=xyzABC123& code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM& code_challenge_method=S256
- User Authentication & Consent: The user logs in at
auth.example.com
and sees a screen asking if "GameTracker" (identified byYOUR_CLIENT_ID
) can access their profile and game library (defined byscope
). The user clicks "Allow". - Redirect Back to Client: The Authorization Server redirects the browser back to the client's specified
redirect_uri
, appending the authorization code and the original state:
(The client MUST verify the receivedhttps://client.example.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyzABC123
state
matches the one it sent in step 1). - Client Exchanges Code for Token: The client's backend server makes a direct POST request to the Authorization Server's token endpoint:
POST /token HTTP/1.1 Host: auth.example.com Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=SplxlOBeZQQYbYS6WxSbIA& redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback& client_id=YOUR_CLIENT_ID& client_secret=YOUR_CLIENT_SECRET& <-- Only for confidential clients code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk <-- The original PKCE secret
- Authorization Server Responds with Tokens: If everything is valid, the Authorization Server responds with the tokens:
{ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA" }
- Client Accesses Resource: The client can now use the
access_token
to make requests to the Resource Server:GET /games HTTP/1.1 Host: api.example.com Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
3.2 Client Credentials Grant
This flow is used when the Client Application needs to access its *own* resources or act on its own behalf (not on behalf of a user), typically for machine-to-machine (M2M) communication.
The Client Application authenticates directly with the Authorization Server's /token
endpoint using its pre-registered client ID and client secret to obtain an Access Token. No user interaction is involved.
Example Request:
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64_ENCODED(CLIENT_ID:CLIENT_SECRET)
grant_type=client_credentials&
scope=read_reports
The response contains an Access Token (but typically no Refresh Token).
3.3 Discouraged/Legacy Grants
- Implicit Grant: Previously common for SPAs, it returns the Access Token directly in the redirect URI fragment. It's now generally discouraged due to security risks (token leakage via browser history/referrers) and lack of support for Refresh Tokens. The Authorization Code Grant with PKCE is the recommended replacement.
- Resource Owner Password Credentials (ROPC) Grant: The Client Application collects the user's username and password directly and sends them to the Authorization Server. This bypasses the primary benefit of OAuth (not sharing credentials) and should only be used for highly trusted first-party applications where redirect-based flows are impossible. It's generally discouraged.
4. Tokens: The Keys to Access
Tokens are central to OAuth 2.0, representing granted permissions.
4.1 Access Token
An Access Token
is a credential used by the Client Application to access protected resources on the Resource Server on behalf of the Resource Owner.
- Purpose: Grants access to specific resources defined by the granted scopes.
- Format: OAuth 2.0 doesn't mandate a specific format, but JSON Web Tokens (JWT) are commonly used, allowing the token itself to contain information (claims) like expiration time, scopes, and user identifier, which can sometimes be validated locally by the Resource Server.
- Lifespan: Typically short-lived (minutes to hours) to limit the window of opportunity if compromised.
- Usage: Sent by the Client in the
Authorization
HTTP header of requests to the Resource Server, usually as a "Bearer" token (e.g.,Authorization: Bearer <access_token>
).
4.2 Refresh Token
A Refresh Token
is a special credential used by the Client Application to obtain a *new* Access Token from the Authorization Server when the current Access Token expires, without requiring the user to re-authenticate.
- Purpose: Allows for extended access periods and improved user experience by avoiding frequent re-logins.
- Lifespan: Typically much longer-lived than Access Tokens (days, weeks, or months).
- Security: Must be stored securely by the Client Application (e.g., encrypted on a backend server) as they represent long-term access potential. They should never be stored in browser local storage for SPAs.
- Usage: Sent only to the Authorization Server's
/token
endpoint (along with client credentials if applicable) to request a new Access Token (and potentially a new Refresh Token - known as refresh token rotation). - Issuance: Not issued in all flows (e.g., typically not in Implicit or Client Credentials). Usually obtained alongside the Access Token in the Authorization Code flow.
5. Scopes: Limiting Permissions
Scopes
are used to limit the amount of access granted to an Access Token. Instead of granting full access, the Client Application requests specific permissions (scopes) it needs (e.g., read_profile
, write_game
, read_contacts
).
The Resource Owner is typically shown these requested scopes during the consent step and can approve or deny them. The issued Access Token will then be associated with the granted scopes, and the Resource Server will enforce these limitations when the token is used to access resources. Requesting only the necessary scopes adheres to the principle of least privilege.
6. Security Considerations
Implementing OAuth 2.0 securely is crucial:
- Use HTTPS Everywhere: All communication involving OAuth (redirects, token exchange, API calls) MUST use TLS/HTTPS to protect codes and tokens in transit.
- Validate Redirect URIs: Authorization Servers must strictly validate the
redirect_uri
provided in requests against pre-registered URIs to prevent codes/tokens from being sent to malicious sites. - Use the `state` Parameter: In redirect-based flows, the Client should generate a unique, unpredictable
state
value, send it in the authorization request, and verify it matches upon receiving the redirect back from the Authorization Server. This helps prevent Cross-Site Request Forgery (CSRF) attacks. - Use PKCE: Always use the Authorization Code grant with PKCE for public clients (SPAs, native apps) to prevent authorization code interception attacks.
- Secure Client Secrets: Confidential clients (e.g., web server apps) must store their client secrets securely and never expose them.
- Secure Token Storage: Store Refresh Tokens securely (e.g., encrypted server-side). Avoid storing Access Tokens or Refresh Tokens in browser local storage for SPAs due to XSS risks. Consider secure HTTP-only cookies or in-memory storage.
- Short-Lived Access Tokens: Keep Access Token lifetimes short to minimize the impact if compromised.
- Token Revocation: Implement mechanisms to revoke Refresh Tokens (and potentially Access Tokens) if they are compromised or no longer needed (e.g., on user logout).
- Scope Limitation: Request only the minimum necessary scopes (principle of least privilege).
7. OAuth 2.0 vs. OpenID Connect (OIDC)
It's important to distinguish OAuth 2.0 from OpenID Connect (OIDC). While related, they serve different primary purposes:
- OAuth 2.0: Focuses on Authorization – granting access to resources. It provides Access Tokens.
- OpenID Connect (OIDC): Focuses on Authentication – verifying the user's identity. It's built *on top of* OAuth 2.0 and adds an
ID Token
(a JWT containing user identity information) alongside the OAuth tokens.
If your goal is purely to allow a client to access an API on behalf of a user, OAuth 2.0 is sufficient. If you need to log the user into the client application itself (authentication), you typically use OIDC.
8. Conclusion
OAuth 2.0 is the standard framework for delegated authorization on the web. By understanding its core roles (Resource Owner, Client, Authorization Server, Resource Server), grant types (especially Authorization Code with PKCE and Client Credentials), the purpose of Access and Refresh Tokens, and the importance of scopes and security best practices, developers can securely allow third-party applications to access user resources without sharing sensitive credentials. It's a fundamental protocol for building modern, interconnected applications and APIs.
9. Additional Resources
Related Articles
- OpenID Connect (OIDC) Fundamentals
- SPA Authentication: JWT vs Cookies
- Understanding the OWASP Top 10 for Web Security
- API Design Best Practices: Versioning and Idempotency