OAuth 2.0 Authentication Guide

Updated: 1/15/2025Category: authentication
oauthsecurityauthenticationsso

OAuth 2.0 Authentication Guide#

A comprehensive guide to understanding OAuth 2.0 and how it's implemented in this authentication system.


Table of Contents#

  1. What is OAuth?
  2. OAuth 2.0 Flow Overview
  3. Supported Providers
  4. Registration Flow
  5. Login Flow
  6. Account Linking Flow
  7. Security Considerations
  8. Implementation Details
  9. Error Handling
  10. Testing OAuth Flows

What is OAuth?#

OAuth 2.0 is an authorization framework that enables applications to obtain limited access to user accounts on an HTTP service. It works by delegating user authentication to the service that hosts the user account and authorizing third-party applications to access that user account.

Key Benefits#

  • No password handling: Your application never sees the user's password
  • Reduced friction: Users can sign in with existing accounts
  • Better security: Leverages established providers' security infrastructure
  • Selective permissions: Users can grant specific access scopes
  • Revocable access: Users can revoke access at any time

OAuth vs Authentication#

While OAuth is technically an authorization protocol, it's commonly used for authentication by:

  1. Asking the provider to confirm the user's identity
  2. Receiving basic profile information (email, name, etc.)
  3. Creating or linking a user account in your system

OAuth 2.0 Flow Overview#

The Authorization Code Flow is the most secure OAuth 2.0 flow for web applications.

Loading diagram...

Key Components#

  1. Authorization Request: User is redirected to provider
  2. User Consent: User approves access at provider
  3. Authorization Code: Provider returns temporary code
  4. Token Exchange: Your app exchanges code for access token
  5. User Info: Your app fetches user profile
  6. Account Creation/Login: User is authenticated in your system

Common OAuth Providers#

Popular OAuth providers and their typical use cases:

Provider Icon Use Case Profile Data
Google 🔍 Most common, reliable Email, name, picture
GitHub 🐙 Developer-focused Username, email, avatar
Apple 🍎 iOS/macOS users Email, name (first time only)
Facebook 📘 Social features Email, name, picture
Twitter 🐦 Social features Username, name, picture

Provider Selection Criteria#

Choose providers based on your audience:

  • Google: Universal choice, high trust
  • GitHub: Developer tools, technical audience
  • Apple: Required for iOS apps, privacy-focused
  • Facebook: Social platforms, broad reach
  • Twitter: Social platforms, real-time features

Registration Flow#

New users can create an account using OAuth.

Loading diagram...

Step-by-Step: Registration#

1. User Initiates OAuth Flow#

typescript
typescript

What happens:

  • Random state token generated for CSRF protection
  • State stored in session (sessionStorage, cookie, etc.)
  • User redirected to provider's authorization page

2. Provider Authorization#

The user sees the provider's consent screen showing:

  • Your app's name and logo
  • Requested permissions (email, profile)
  • Option to approve or deny

3. Callback Handling#

typescript
typescript

What happens:

  • State token verified (CSRF protection)
  • Authorization code exchanged for access token
  • User profile fetched from provider
  • New user account created (or existing user found)
  • Session created or tokens issued
  • User redirected to application

Login Flow#

Existing users can sign in using their linked OAuth accounts.

Loading diagram...

Login vs Registration#

The OAuth flow is identical for login and registration. The backend automatically:

  • Finds existing user by OAuth provider + provider user ID
  • Creates new user if no match found
  • Links OAuth account to existing user if email matches

This provides a seamless experience where users don't need to know whether they're "logging in" or "registering".


Account Linking Flow#

Authenticated users can link additional OAuth providers to their account.

Loading diagram...

Key Differences from Registration#

  1. Authentication Required: User must be logged in with active session
  2. Link Mode Flag: Flag set to indicate this is a linking operation (not registration)
  3. Session Authentication: User's existing session used to authenticate the linking request
  4. Conflict Detection: Check if OAuth account already linked to prevent duplicates
  5. Different Callback: Separate callback URL for account linking flow

The backend validates:

typescript
typescript

Unlinking Accounts#

Users can unlink OAuth accounts with protection:

typescript
typescript

Last Auth Method Protection: Users cannot unlink their only authentication method. They must either:

  • Set a password first
  • Link another OAuth account
  • Add another auth method (passkey, phone, etc.)

Security Considerations#

1. CSRF Protection with State Parameter#

Loading diagram...

Why it matters: Without state verification, an attacker could:

  1. Initiate their own OAuth flow
  2. Capture the callback URL with authorization code
  3. Trick victim into visiting that URL
  4. Link attacker's OAuth account to victim's profile

How we prevent it:

typescript
typescript

2. HTTPS Only#

All OAuth flows must use HTTPS because:

  • Authorization codes are sensitive
  • Access tokens are transmitted
  • Man-in-the-middle attacks would be trivial on HTTP

3. Short-lived Authorization Codes#

Authorization codes are:

  • Single-use only
  • Expire after 10 minutes
  • Must be exchanged immediately

4. Token Storage#

Tokens can be stored in different ways, each with trade-offs:

localStorage/sessionStorage:

typescript
typescript
  • ✅ Survives page refreshes
  • ✅ Works across tabs
  • ⚠️ Vulnerable to XSS attacks
  • ⚠️ Accessible to JavaScript

httpOnly Cookies:

typescript
typescript
  • ✅ Not accessible to JavaScript
  • ✅ Automatic transmission with requests
  • ⚠️ Vulnerable to CSRF (mitigated with sameSite)
  • ⚠️ Size limitations

XSS Mitigation:

  • Content Security Policy (CSP) headers
  • Input sanitization
  • Regular security audits
  • No inline scripts
  • Trusted Types API

5. Redirect URI Validation#

Backend validates that redirect_uri matches:

  • Exact URL registered with provider
  • Your application's domain
  • No open redirects

6. Scope Minimization#

Only request necessary scopes:

typescript
typescript

Avoid requesting unnecessary permissions that users might reject.


Implementation Details#

Typical Backend API Endpoints#

text

Database Schema#

typescript
typescript

Provider-Specific Configuration#

Each provider requires:

typescript
typescript

Example for GitHub:

typescript
typescript

Error Handling#

Common Error Scenarios#

Loading diagram...

Error Messages#

User-friendly error messages for each scenario:

Error Code User Message Action
access_denied "Authorization was cancelled. No changes were made to your account." Offer retry
invalid_state "Security validation failed. Please try again." Clear state, retry
invalid_code "This authorization link has expired. Please try again." Restart flow
USER_EXISTS "An account with this email already exists. Please sign in instead." Link to login
OAUTH_ACCOUNT_IN_USE "This account is already linked to another user." Suggest different account
OAUTH_ALREADY_LINKED "This account is already linked to your profile." Just info
LAST_AUTH_METHOD "Cannot unlink your only authentication method. Please set a password or link another account first." Explain requirement

Error Logging#

Backend logs all OAuth errors for debugging:

typescript
typescript

Testing OAuth Flows#

E2E Testing Example#

Example OAuth testing with Cypress:

typescript
typescript

Manual Testing Checklist#

When setting up a new OAuth provider:

  • Register app in provider's developer console
  • Configure redirect URIs
  • Set environment variables (client ID, secret)
  • Test registration flow
    • Can create new account
    • Profile data populated correctly
    • Email verified status handled
  • Test login flow
    • Can sign in with linked account
    • Correct user loaded
    • Tokens issued properly
  • Test account linking
    • Can link to existing account
    • Duplicate detection works
    • Cannot link account used by other user
  • Test unlinking
    • Can unlink when other methods exist
    • Cannot unlink last auth method
    • Can re-link same account
  • Test error scenarios
    • User denies permission
    • Invalid state parameter
    • Expired authorization code
    • Network errors

Provider Testing Environments#

Most OAuth providers offer test/sandbox modes:

Provider Test Mode Notes
Google Yes Use test users in Google Cloud Console
GitHub No Use personal account or create test org
Apple Yes Sandbox environment with test accounts
Facebook Yes Test apps with test users
Twitter No Use development app with personal account

OAuth Best Practices#

1. Always Use State Parameter#

typescript
typescript

2. Validate All Callback Parameters#

typescript
typescript

3. Handle Email Verification#

Some providers return unverified emails or no email at all:

typescript
typescript

4. Graceful Degradation#

typescript
typescript

5. Token Refresh#

Store refresh tokens for long-lived access:

typescript
typescript

6. Audit Logging#

Log all OAuth events for security auditing:

typescript
typescript

Common OAuth Pitfalls#

1. Not Validating Redirect URI#

Problem: Accepting any redirect URI enables open redirect attacks

Solution: Whitelist exact redirect URIs

typescript
typescript

2. Storing Tokens in Plain Text#

Problem: Database breach exposes access to user accounts

Solution: Encrypt tokens at rest

typescript
typescript

3. Not Handling Email Changes#

Problem: User changes email at provider, causing duplicate accounts

Solution: Use provider's unique user ID, not email

typescript
typescript

4. Assuming Email is Always Present#

Problem: Some providers don't always return email

Solution: Handle missing email gracefully

typescript
typescript

5. Not Implementing Token Expiration#

Problem: Old access tokens work forever, can't revoke access

Solution: Implement token refresh and expiration

typescript
typescript

Advanced Topics#

OAuth with PKCE#

For mobile apps or public clients, use PKCE (Proof Key for Code Exchange):

typescript
typescript

OpenID Connect (OIDC)#

Enhanced OAuth 2.0 with ID tokens:

typescript
typescript

Multi-tenant OAuth#

Support multiple OAuth apps per provider:

typescript
typescript

Debugging OAuth Issues#

Enable Debug Logging#

typescript
typescript

Common Issues and Solutions#

Issue: "redirect_uri_mismatch"

text

Issue: "invalid_client"

text

Issue: "invalid_grant" or "code expired"

text

Issue: User profile missing email

text

OAuth Debug Checklist#

  1. ✅ Client ID and secret correct?
  2. ✅ Redirect URI matches exactly?
  3. ✅ Using HTTPS (except localhost)?
  4. ✅ State parameter generated and verified?
  5. ✅ Authorization code exchanged immediately?
  6. ✅ Correct scopes requested?
  7. ✅ Server time in sync (for token expiration)?
  8. ✅ CORS headers configured correctly?
  9. ✅ Error messages logged on backend?
  10. ✅ Network requests visible in browser DevTools?

Resources and Further Reading#

Official Documentation#

Security Resources#

Tools#

Implementation Examples#


Summary#

OAuth 2.0 provides secure, user-friendly authentication by:

  1. Delegating authentication to trusted providers
  2. Protecting user passwords - you never handle them
  3. Reducing friction - users sign in with existing accounts
  4. Enabling account linking - multiple sign-in methods per user
  5. Maintaining security - CSRF protection, HTTPS, token encryption

Key implementation requirements:

  • ✅ HTTPS in production
  • ✅ State parameter for CSRF protection
  • ✅ Redirect URI validation
  • ✅ Secure token storage
  • ✅ Proper error handling
  • ✅ Email verification handling
  • ✅ Last auth method protection

With proper implementation, OAuth 2.0 provides a robust, secure, and user-friendly authentication solution.


Happy authenticating! 🔐

Viewing as guest