Documentation/Authentication Flows

Authentication Flows

Understand the different OAuth 2.0 flows, when to use them, and how they work with OAuth42.

Overview

OAuth 2.0 defines several authentication flows (also called "grant types") for different use cases. Choosing the right flow depends on your application type, security requirements, and user experience goals.

OAuth42 implements all standard OAuth 2.0 flows and follows current security best practices, including mandatory PKCE for public clients and deprecation of legacy flows.

Authorization Code Flow

Recommended for Server-Side Applications

Most secure flow for web applications with a backend server that can securely store client secrets.

How It Works

1.
User initiates login
Application redirects to OAuth42 authorization endpoint
2.
User authenticates and consents
User logs in and approves requested permissions
3.
Authorization code returned
OAuth42 redirects back with a short-lived authorization code
4.
Backend exchanges code for tokens
Server makes POST request with code + client secret
5.
Tokens issued
OAuth42 returns access token, refresh token, and ID token

Code Example

// Step 1: Redirect to authorization endpoint
const authUrl = new URL('https://oauth42.com/oauth2/authorize');
authUrl.searchParams.set('client_id', 'your_client_id');
authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('state', randomState);

window.location.href = authUrl.toString();

// Step 2: Handle callback on your backend
app.get('/callback', async (req, res) => {
  const { code, state } = req.query;

  // Verify state to prevent CSRF
  if (state !== storedState) {
    return res.status(400).send('Invalid state');
  }

  // Step 3: Exchange code for tokens
  const response = await fetch('https://oauth42.com/oauth2/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code: code,
      redirect_uri: 'https://yourapp.com/callback',
      client_id: 'your_client_id',
      client_secret: 'your_client_secret', // Secure on server only!
    }),
  });

  const tokens = await response.json();
  // tokens.access_token, tokens.refresh_token, tokens.id_token
});

Security Benefits

  • Client secret never exposed to browser
  • Authorization code is single-use and short-lived (10 minutes)
  • Tokens are issued directly to backend server
  • State parameter prevents CSRF attacks

Authorization Code Flow with PKCE

Recommended for Single-Page Apps & Mobile Apps

Enhanced security flow for public clients that cannot securely store a client secret.

What is PKCE?

PKCE (Proof Key for Code Exchange, pronounced "pixy") is an extension to the Authorization Code Flow that prevents authorization code interception attacks. It's now mandatory for all public clients in OAuth42.

How PKCE Works

1.
Generate code verifier
Random string (43-128 characters)
code_verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
2.
Generate code challenge
SHA256 hash of verifier, base64url encoded
code_challenge = base64url(sha256(code_verifier))
3.
Send challenge with authorization request
Include code_challenge and code_challenge_method=S256
4.
Send verifier with token request
OAuth42 validates that sha256(verifier) matches challenge

Code Example

// Step 1: Generate PKCE parameters
function generateCodeVerifier() {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return base64UrlEncode(array);
}

async function generateCodeChallenge(verifier) {
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);
  const hash = await crypto.subtle.digest('SHA-256', data);
  return base64UrlEncode(new Uint8Array(hash));
}

const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);

// Store verifier for later (session storage, not localStorage!)
sessionStorage.setItem('code_verifier', codeVerifier);

// Step 2: Redirect with PKCE parameters
const authUrl = new URL('https://oauth42.com/oauth2/authorize');
authUrl.searchParams.set('client_id', 'your_client_id');
authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
authUrl.searchParams.set('state', randomState);

window.location.href = authUrl.toString();

// Step 3: Exchange code for tokens with verifier
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const codeVerifier = sessionStorage.getItem('code_verifier');

const response = await fetch('https://oauth42.com/oauth2/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: code,
    redirect_uri: 'https://yourapp.com/callback',
    client_id: 'your_client_id',
    code_verifier: codeVerifier, // No client secret needed!
  }),
});

const tokens = await response.json();

Why PKCE is Essential

  • Prevents authorization code interception attacks
  • No client secret needed (safe for SPAs and mobile apps)
  • Attacker cannot use stolen code without the verifier
  • Required by OAuth 2.1 specification

Client Credentials Flow

🤖
For Machine-to-Machine Communication

Used when applications need to access their own resources, not user data.

Use Cases

✓ Backend Services

Microservices communicating with each other

✓ CLI Tools

Command-line applications accessing APIs

✓ Scheduled Jobs

Cron jobs or background tasks

✓ IoT Devices

Devices reporting data to backend

Code Example

// Direct token request with client credentials
const response = await fetch('https://oauth42.com/oauth2/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Basic ' + btoa(`${clientId}:${clientSecret}`),
  },
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    scope: 'api:read api:write', // Application-level scopes only
  }),
});

const tokens = await response.json();
// tokens.access_token (no refresh token or ID token)

// Use access token for API requests
const apiResponse = await fetch('https://api.yourservice.com/resources', {
  headers: {
    'Authorization': `Bearer ${tokens.access_token}`,
  },
});

Important Notes

  • ⚠️No user context - tokens represent the application itself
  • ⚠️No refresh tokens issued (request new token when expired)
  • ⚠️Client secret must be stored securely
  • ⚠️Only use for trusted backend services, never in browser or mobile apps

Implicit Flow

Deprecated - Do Not Use

This flow has security vulnerabilities and is deprecated in OAuth 2.1. Use Authorization Code Flow with PKCE instead.

Why It's Deprecated

  • Access tokens exposed in browser history:Tokens returned in URL fragment can be logged in browser history, proxy logs, and referrer headers
  • No refresh tokens:Users must re-authenticate when access token expires
  • Vulnerable to token theft:No protection against authorization code interception attacks

Migration Path

If you're currently using Implicit Flow, migrate to Authorization Code Flow with PKCE. It provides better security while maintaining the same user experience. OAuth42 will continue to support Implicit Flow for backward compatibility but strongly recommends migrating.

Resource Owner Password Flow

Deprecated - Use Only as Last Resort

This flow requires users to share passwords with third-party applications. Only use for highly trusted first-party applications.

Why It's Problematic

  • Breaks OAuth security model:User credentials are exposed to the application
  • No consent screen:Users cannot review or limit permissions granted
  • Phishing risk:Trains users to enter passwords in third-party forms

When It Might Be Acceptable

Only consider this flow for:

  • First-party applications you fully control and trust
  • Migration from legacy authentication systems
  • Environments where browser redirects are not possible

Better alternative: Use Authorization Code Flow with PKCE, even for native mobile apps. Modern OAuth libraries handle this seamlessly.

Flow Comparison

FlowUse CaseSecurityRefresh TokenStatus
Authorization CodeWeb apps with backendHigh✓ Yes✓ Recommended
Authorization Code + PKCESPAs, mobile appsHigh✓ Yes✓ Recommended
Client CredentialsMachine-to-machineHigh✗ No✓ Recommended
ImplicitLegacy SPAsLow✗ No⛔ Deprecated
PasswordFirst-party apps onlyLow✓ Yes⛔ Deprecated

Which Flow Should I Use?

🌐 Building a web application with a backend server?
→ Use Authorization Code Flow
Store client secret securely on server
📱 Building a single-page app (React, Vue, Angular)?
→ Use Authorization Code Flow with PKCE
No backend required, no secrets exposed
📱 Building a mobile app (iOS, Android, React Native)?
→ Use Authorization Code Flow with PKCE
Use system browser or custom tabs for security
🤖 Building a backend service or API integration?
→ Use Client Credentials Flow
No user context, machine-to-machine only
⚠️ Have a legacy application using Implicit Flow?
→ Migrate to Authorization Code Flow with PKCE
Implicit flow will be removed in OAuth 2.1

Best Practices

Always Use HTTPS

All OAuth 2.0 flows require HTTPS in production. Never use HTTP for authentication flows as credentials and tokens can be intercepted.

Validate State Parameter

Always include and validate the state parameter to prevent CSRF attacks. Generate a random value and verify it matches when handling the callback.

Use Short-Lived Access Tokens

Keep access token lifetime short (15-60 minutes) and use refresh tokens to obtain new ones. This limits the window of opportunity if a token is compromised.

Implement Token Rotation

When using refresh tokens, implement rotation: issue a new refresh token each time one is used. This helps detect token theft and replay attacks.

Secure Client Credentials

Never hardcode client secrets in source code. Use environment variables or secure key management systems. Rotate secrets regularly.

Limit Token Scope

Request only the scopes your application needs. Users are more likely to approve limited permissions, and it reduces risk if tokens are compromised.

Next Steps