Modern Authentication Patterns: OAuth, OIDC, and Beyond

Authentication vs Authorization

Authentication verifies identity (who you are). Authorization determines permissions (what you can do). Modern systems need both.

OAuth 2.0 Flows

Authorization Code Flow (Web Apps)

// Step 1: Redirect to authorization server
const authUrl = new URL('https://auth.example.com/authorize');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('redirect_uri', 'https://app.example.com/callback');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('state', generateState());

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

// Step 2: Exchange code for tokens (server-side)
app.get('/callback', async (req, res) => {
  const { code, state } = req.query;
  
  // Verify state to prevent CSRF
  if (state !== req.session.state) {
    return res.status(400).send('Invalid state');
  }
  
  const tokens = await fetch('https://auth.example.com/token', {
    method: 'POST',
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code,
      redirect_uri: 'https://app.example.com/callback',
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET
    })
  }).then(r => r.json());
  
  req.session.tokens = tokens;
  res.redirect('/dashboard');
});

Authorization Code with PKCE (SPAs, Mobile)

// Generate PKCE challenge
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));
}

// Include in auth request
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);

authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');

// Store verifier for token exchange
sessionStorage.setItem('code_verifier', codeVerifier);

OpenID Connect

ID Token Validation

import * as jose from 'jose';

async function validateIdToken(idToken) {
  const JWKS = jose.createRemoteJWKSet(
    new URL('https://auth.example.com/.well-known/jwks.json')
  );
  
  const { payload } = await jose.jwtVerify(idToken, JWKS, {
    issuer: 'https://auth.example.com',
    audience: CLIENT_ID
  });
  
  // Additional validations
  if (payload.nonce !== expectedNonce) {
    throw new Error('Invalid nonce');
  }
  
  if (payload.exp < Date.now() / 1000) {
    throw new Error('Token expired');
  }
  
  return payload;
}

Session Management

// Secure session configuration
import session from 'express-session';
import RedisStore from 'connect-redis';

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  name: '__Host-session',  // Cookie prefix for security
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true,          // HTTPS only
    httpOnly: true,        // No JS access
    sameSite: 'strict',    // CSRF protection
    maxAge: 3600000        // 1 hour
  }
}));

Token Refresh

async function refreshTokens(refreshToken) {
  const response = await fetch('https://auth.example.com/token', {
    method: 'POST',
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
      client_id: CLIENT_ID
    })
  });
  
  if (!response.ok) {
    throw new Error('Token refresh failed');
  }
  
  return response.json();
}

// Automatic refresh middleware
async function ensureValidToken(req, res, next) {
  const tokens = req.session.tokens;
  
  if (isTokenExpired(tokens.access_token)) {
    try {
      req.session.tokens = await refreshTokens(tokens.refresh_token);
    } catch (error) {
      return res.redirect('/login');
    }
  }
  
  next();
}

Passwordless Authentication

WebAuthn / Passkeys

// Registration
async function registerPasskey() {
  const options = await fetch('/auth/webauthn/register-options').then(r => r.json());
  
  const credential = await navigator.credentials.create({
    publicKey: {
      challenge: Uint8Array.from(options.challenge),
      rp: { name: 'My App', id: 'app.example.com' },
      user: {
        id: Uint8Array.from(options.userId),
        name: options.username,
        displayName: options.displayName
      },
      pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
      authenticatorSelection: {
        authenticatorAttachment: 'platform',
        userVerification: 'required'
      }
    }
  });
  
  await fetch('/auth/webauthn/register', {
    method: 'POST',
    body: JSON.stringify(credential)
  });
}

Multi-Factor Authentication

import { authenticator } from 'otplib';

// Generate TOTP secret
const secret = authenticator.generateSecret();
const otpauth = authenticator.keyuri(user.email, 'MyApp', secret);

// Verify TOTP code
function verifyTOTP(secret, token) {
  return authenticator.verify({ token, secret });
}

Conclusion

Choose the right authentication pattern for your use case. OAuth 2.0 with PKCE for SPAs, server-side tokens for web apps, and consider passwordless for better UX and security.

๐Ÿ“šTechnical Insights & Industry Trends

Insights & Knowledge Hub

Explore in-depth articles on software development, digital transformation, and emerging technologies. Learn from real-world experience.

Browse by Topic

๐Ÿ…ฐ๏ธ
ArchitectureJan 2024

Why Angular SSR Beats React for SEO in 2024

A comprehensive comparison of server-side rendering implementations and their impact on search engine optimization, performance, and user experience.

๐Ÿ”Œ
ArchitectureDec 2023

Microservices: When to Use and When to Avoid

Understanding the trade-offs of microservices architecture and making the right choice for your organization's scale, complexity, and team structure.

๐Ÿ—๏ธ
System ArchitectureJan 2024

Essential Design Patterns for Enterprise Systems

Master the fundamental design patterns that every software architect should know for building scalable, maintainable enterprise applications.

๐Ÿ—๏ธ
System ArchitectureJan 2024

Domain-Driven Design: A Practical Guide

Learn how to apply Domain-Driven Design principles to create software that truly reflects business requirements and scales with organizational complexity.

๐Ÿ—๏ธ
System ArchitectureDec 2023

Event-Driven Architecture: Complete Implementation Guide

Build loosely coupled, scalable systems using event-driven architecture patterns with practical examples using Kafka, RabbitMQ, and Azure Service Bus.

๐Ÿ—๏ธ
System ArchitectureDec 2023

RESTful API Design: Best Practices and Patterns

Design APIs that developers love to use. Learn REST principles, versioning strategies, error handling, and documentation best practices.

Deep Dives into Key Areas

๐Ÿ—๏ธ

System Architecture

Design patterns and architectural decisions

12 Articles
โ˜๏ธ

Cloud Computing

AWS, Azure, and cloud-native development

8 Articles
โšก

Performance

Optimization and scalability techniques

6 Articles
๐Ÿค–

AI & Machine Learning

Practical AI applications in enterprise

5 Articles
๐Ÿ”’

Security

Best practices and compliance

7 Articles
๐Ÿ“ฑ

Modern Frontend

Angular, React, and UI/UX best practices

10 Articles

Have a Project in Mind?

Let's turn your ideas into reality. Schedule a free consultation to discuss how we can help transform your business.