Oauth Implementation

by mindrally97184105b5daNo license269 starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 5 weeks ago

Guidelines for implementing OAuth 2.0 and OAuth 2.1 authentication flows with security best practices and PKCE

Instructions onlySoftware Development
AI-generated overview

Guidelines and code examples for implementing OAuth 2.0 and OAuth 2.1 authorization flows with PKCE.

What it does
Provides implementation guidance for OAuth 2.0 and OAuth 2.1 authentication flows, centered on the authorization code flow with PKCE. It includes JavaScript code samples for generating PKCE parameters, building authorization requests, handling callbacks, exchanging codes for tokens, validating JWTs, and rotating refresh tokens. It also covers redirect URI validation, scope management, token storage, error handling, and a testing checklist.
When to use it
Use when adding OAuth sign-in or token handling to an application and you want secure, standards-aligned patterns. It suits client-side and server-side authorization code flow work, including PKCE setup, token validation, and refresh token rotation.
Requirements
No scripts are shipped; it is instructions and code examples only. Implementing the examples requires a JavaScript runtime or browser, and server-side samples reference Node.js, Express, jsonwebtoken, and jwks-rsa. Network access to an OAuth authorization server, token endpoint, and JWKS endpoint is needed, along with client credentials such as a client ID and secret.

OAuth Implementation

You are an expert in OAuth 2.0 and OAuth 2.1 implementation. Follow these guidelines when implementing OAuth authentication flows.

Core Principles

  • Always use OAuth 2.1 patterns (PKCE required, no implicit flow)
  • Use HTTPS for all OAuth communications
  • Implement proper state management for CSRF protection
  • Follow the principle of least privilege for scopes
  • Validate all tokens server-side

OAuth 2.1 Key Requirements

OAuth 2.1 consolidates best practices and deprecates insecure patterns:

  • PKCE is required for ALL clients using authorization code flow
  • Implicit grant is removed
  • Resource Owner Password Credentials grant is removed
  • Redirect URIs must use exact string matching
  • Refresh tokens must be sender-constrained or use rotation

Authorization Code Flow with PKCE

Step 1: Generate PKCE Parameters

javascript
// Generate cryptographically secure code verifierfunction generateCodeVerifier() {  const array = new Uint8Array(32);  crypto.getRandomValues(array);  return base64URLEncode(array);}
// Create code challenge from verifierasync function generateCodeChallenge(verifier) {  const encoder = new TextEncoder();  const data = encoder.encode(verifier);  const digest = await crypto.subtle.digest('SHA-256', data);  return base64URLEncode(new Uint8Array(digest));}
function base64URLEncode(buffer) {  return btoa(String.fromCharCode(...buffer))    .replace(/\+/g, '-')    .replace(/\//g, '_')    .replace(/=+$/, '');}

Step 2: Authorization Request

javascript
async function initiateOAuthFlow() {  const codeVerifier = generateCodeVerifier();  const codeChallenge = await generateCodeChallenge(codeVerifier);  const state = generateSecureRandomString();
  // Store for later verification  sessionStorage.setItem('oauth_code_verifier', codeVerifier);  sessionStorage.setItem('oauth_state', state);
  const params = new URLSearchParams({    response_type: 'code',    client_id: CLIENT_ID,    redirect_uri: REDIRECT_URI,    scope: 'openid profile email',    state: state,    code_challenge: codeChallenge,    code_challenge_method: 'S256',  });
  window.location.href = `${AUTHORIZATION_ENDPOINT}?${params}`;}

Step 3: Handle Callback and Token Exchange

javascript
async function handleCallback() {  const params = new URLSearchParams(window.location.search);  const code = params.get('code');  const state = params.get('state');  const error = params.get('error');
  // Check for errors  if (error) {    throw new Error(`OAuth error: ${error} - ${params.get('error_description')}`);  }
  // Validate state to prevent CSRF  const storedState = sessionStorage.getItem('oauth_state');  if (state !== storedState) {    throw new Error('Invalid state parameter - possible CSRF attack');  }
  // Retrieve code verifier  const codeVerifier = sessionStorage.getItem('oauth_code_verifier');
  // Exchange code for tokens  const response = await fetch(TOKEN_ENDPOINT, {    method: 'POST',    headers: {      'Content-Type': 'application/x-www-form-urlencoded',    },    body: new URLSearchParams({      grant_type: 'authorization_code',      code: code,      redirect_uri: REDIRECT_URI,      client_id: CLIENT_ID,      code_verifier: codeVerifier,    }),  });
  if (!response.ok) {    throw new Error('Token exchange failed');  }
  const tokens = await response.json();
  // Clean up  sessionStorage.removeItem('oauth_code_verifier');  sessionStorage.removeItem('oauth_state');
  return tokens;}

Server-Side Implementation

Confidential Client Token Exchange

javascript
// Node.js/Express exampleapp.post('/oauth/callback', async (req, res) => {  const { code, state } = req.body;
  // Validate state  if (state !== req.session.oauthState) {    return res.status(400).json({ error: 'Invalid state' });  }
  try {    const tokenResponse = await fetch(TOKEN_ENDPOINT, {      method: 'POST',      headers: {        'Content-Type': 'application/x-www-form-urlencoded',        // Client authentication for confidential clients        Authorization: `Basic ${Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64')}`,      },      body: new URLSearchParams({        grant_type: 'authorization_code',        code: code,        redirect_uri: REDIRECT_URI,        code_verifier: req.session.codeVerifier,      }),    });
    const tokens = await tokenResponse.json();
    // Store tokens securely server-side    req.session.accessToken = tokens.access_token;    req.session.refreshToken = tokens.refresh_token;
    res.redirect('/dashboard');  } catch (error) {    res.status(500).json({ error: 'Token exchange failed' });  }});

Token Security Best Practices

Access Token Validation

javascript
const jwt = require('jsonwebtoken');const jwksClient = require('jwks-rsa');
const client = jwksClient({  jwksUri: `${ISSUER}/.well-known/jwks.json`,  cache: true,  cacheMaxAge: 600000, // 10 minutes});
function getSigningKey(header, callback) {  client.getSigningKey(header.kid, (err, key) => {    if (err) return callback(err);    const signingKey = key.getPublicKey();    callback(null, signingKey);  });}
async function validateToken(token) {  return new Promise((resolve, reject) => {    jwt.verify(      token,      getSigningKey,      {        audience: EXPECTED_AUDIENCE,        issuer: EXPECTED_ISSUER,        algorithms: ['RS256'], // Whitelist allowed algorithms      },      (err, decoded) => {        if (err) reject(err);        else resolve(decoded);      }    );  });}

Refresh Token Rotation

javascript
async function refreshAccessToken(refreshToken) {  const response = await fetch(TOKEN_ENDPOINT, {    method: 'POST',    headers: {      'Content-Type': 'application/x-www-form-urlencoded',    },    body: new URLSearchParams({      grant_type: 'refresh_token',      refresh_token: refreshToken,      client_id: CLIENT_ID,    }),  });
  if (!response.ok) {    // Refresh token may be expired or revoked    throw new Error('Refresh token invalid');  }
  const tokens = await response.json();
  // If rotation is enabled, you'll receive a new refresh token  // Store the new refresh token and invalidate the old one  return tokens;}

Security Requirements

Redirect URI Validation

javascript
// Server-side: validate redirect URIs against whitelistconst ALLOWED_REDIRECT_URIS = [  'https://myapp.com/callback',  'https://myapp.com/oauth/callback',];
function validateRedirectUri(uri) {  // Exact string matching - no wildcards  return ALLOWED_REDIRECT_URIS.includes(uri);}

Scope Management

javascript
// Request minimum necessary scopesconst SCOPES = {  basic: 'openid profile email',  readOnly: 'openid profile email read:data',  fullAccess: 'openid profile email read:data write:data',};
// Validate scopes on the serverfunction validateScopes(requestedScopes, allowedScopes) {  const requested = requestedScopes.split(' ');  const allowed = allowedScopes.split(' ');  return requested.every(scope => allowed.includes(scope));}

Common Vulnerabilities to Prevent

1. Authorization Code Injection

Always use PKCE - the code_verifier ensures only the original requester can exchange the code.

2. CSRF Attacks

javascript
// Always use and validate the state parameterconst state = crypto.randomBytes(32).toString('hex');// Store in session and validate on callback

3. Open Redirect

javascript
// Never construct redirect URIs from user input// Always use whitelisted URIsconst redirectUri = ALLOWED_REDIRECT_URIS[0]; // Don't: req.query.redirect_uri

4. Token Leakage

javascript
// Never log tokensconsole.log('User authenticated'); // Goodconsole.log(`Token: ${accessToken}`); // NEVER DO THIS
// Don't include tokens in URLs// Use Authorization header insteadfetch('/api/resource', {  headers: {    Authorization: `Bearer ${accessToken}`,  },});

Token Storage Recommendations

Browser Applications

javascript
// Option 1: Memory (most secure, but lost on refresh)let accessToken = null;
// Option 2: HttpOnly cookies (requires backend)// Set by server with appropriate flags// Secure, HttpOnly, SameSite=Strict
// Option 3: sessionStorage (cleared when tab closes)sessionStorage.setItem('access_token', token);
// Avoid localStorage for sensitive tokens// Vulnerable to XSS attacks

Server Applications

javascript
// Store tokens encrypted in session or databaseconst encryptedToken = encrypt(accessToken, SESSION_ENCRYPTION_KEY);req.session.encryptedAccessToken = encryptedToken;

Error Handling

javascript
const OAUTH_ERRORS = {  invalid_request: 'The request is missing a required parameter',  unauthorized_client: 'The client is not authorized',  access_denied: 'The user denied the request',  unsupported_response_type: 'The response type is not supported',  invalid_scope: 'The requested scope is invalid',  server_error: 'The authorization server encountered an error',  temporarily_unavailable: 'The server is temporarily unavailable',};
function handleOAuthError(error, errorDescription) {  const message = OAUTH_ERRORS[error] || 'Unknown error';  console.error(`OAuth Error: ${message}. Details: ${errorDescription}`);  // Show user-friendly error message}

Testing Checklist

  • PKCE flow works correctly
  • State parameter is validated
  • Invalid state is rejected
  • Token expiration is handled
  • Refresh token rotation works
  • Invalid tokens are rejected
  • Scopes are properly enforced
  • Redirect URIs are validated
  • Error cases are handled gracefully

Source and attribution

Source:mindrally/skillsinoauth-implementationat commit9718410

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal