0% read
Skip to main content
Authentication and Authorization - Production Patterns for Secure Applications

Authentication and Authorization - Production Patterns for Secure Applications

Master authentication and authorization with OAuth 2.0, JWT tokens, RBAC, ABAC, session management, passwordless authentication, and security best practices for production applications.

S
StaticBlock Editorial
23 min read

Authentication and authorization form the foundation of application security, controlling who can access your system and what they can do once authenticated. This comprehensive guide covers production-ready patterns for implementing secure authentication and authorization in modern applications.

Authentication vs Authorization

Authentication verifies who you are - confirming a user's identity through credentials like passwords, biometrics, or tokens.

Authorization determines what you can do - defining which resources and actions an authenticated user can access.

These concepts work together but serve distinct purposes. Poor implementation of either creates security vulnerabilities that attackers exploit.

OAuth 2.0 and OpenID Connect

OAuth 2.0 provides industry-standard authorization flows for delegated access, while OpenID Connect (OIDC) adds an identity layer for authentication.

Authorization Code Flow

The most secure OAuth 2.0 flow for web applications:

// Express.js OAuth 2.0 authorization code flow
import express from 'express';
import axios from 'axios';
import crypto from 'crypto';

const app = express();

// OAuth 2.0 configuration const OAUTH_CONFIG = { clientId: process.env.OAUTH_CLIENT_ID!, clientSecret: process.env.OAUTH_CLIENT_SECRET!, authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth', tokenEndpoint: 'https://oauth2.googleapis.com/token', redirectUri: 'http://localhost:3000/callback', scope: 'openid profile email' };

// Initiate OAuth flow app.get('/login', (req, res) => { const state = crypto.randomBytes(16).toString('hex'); const codeVerifier = crypto.randomBytes(32).toString('base64url'); const codeChallenge = crypto .createHash('sha256') .update(codeVerifier) .digest('base64url');

// Store state and code verifier in session req.session.oauthState = state; req.session.codeVerifier = codeVerifier;

const authUrl = new URL(OAUTH_CONFIG.authorizationEndpoint); authUrl.searchParams.set('client_id', OAUTH_CONFIG.clientId); authUrl.searchParams.set('redirect_uri', OAUTH_CONFIG.redirectUri); authUrl.searchParams.set('response_type', 'code'); authUrl.searchParams.set('scope', OAUTH_CONFIG.scope); authUrl.searchParams.set('state', state); authUrl.searchParams.set('code_challenge', codeChallenge); authUrl.searchParams.set('code_challenge_method', 'S256');

res.redirect(authUrl.toString()); });

// Handle OAuth callback app.get('/callback', async (req, res) => { const { code, state } = req.query;

// Validate state parameter to prevent CSRF if (state !== req.session.oauthState) { return res.status(400).send('Invalid state parameter'); }

try { // Exchange authorization code for access token const tokenResponse = await axios.post(OAUTH_CONFIG.tokenEndpoint, { grant_type: 'authorization_code', code, redirect_uri: OAUTH_CONFIG.redirectUri, client_id: OAUTH_CONFIG.clientId, client_secret: OAUTH_CONFIG.clientSecret, code_verifier: req.session.codeVerifier });

const { access_token, refresh_token, id_token } = tokenResponse.data;

// Decode and verify ID token (contains user identity)
const userInfo = await verifyIdToken(id_token);

// Store tokens securely
req.session.accessToken = access_token;
req.session.refreshToken = refresh_token;
req.session.user = userInfo;

res.redirect('/dashboard');

} catch (error) { console.error('OAuth error:', error); res.status(500).send('Authentication failed'); } });

Key security features:

  • PKCE (Proof Key for Code Exchange): Protects against authorization code interception
  • State parameter: Prevents CSRF attacks
  • HTTPS-only: Protects tokens in transit
  • Short-lived access tokens: Limits damage from token theft

Token Refresh Flow

Access tokens expire quickly (typically 15-60 minutes). Use refresh tokens to obtain new access tokens without re-authentication:

async function refreshAccessToken(refreshToken: string): Promise<string> {
  try {
    const response = await axios.post(OAUTH_CONFIG.tokenEndpoint, {
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
      client_id: OAUTH_CONFIG.clientId,
      client_secret: OAUTH_CONFIG.clientSecret
    });
return response.data.access_token;

} catch (error) { // Refresh token expired or revoked - require re-authentication throw new Error('Refresh token invalid'); } }

// Middleware to ensure valid access token async function ensureValidToken(req, res, next) { const accessToken = req.session.accessToken;

// Check if token is expired (decode JWT and check exp claim) if (isTokenExpired(accessToken)) { try { const newAccessToken = await refreshAccessToken(req.session.refreshToken); req.session.accessToken = newAccessToken; } catch (error) { return res.redirect('/login'); } }

next(); }

JSON Web Tokens (JWT)

JWTs provide stateless authentication by encoding user identity and claims in a signed token.

JWT Structure and Signing

import jwt from 'jsonwebtoken';
import { promisify } from 'util';

const JWT_SECRET = process.env.JWT_SECRET!; const JWT_EXPIRES_IN = '15m'; const REFRESH_TOKEN_EXPIRES_IN = '7d';

interface UserPayload { userId: string; email: string; roles: string[]; }

// Generate access token function generateAccessToken(user: UserPayload): string { return jwt.sign( { sub: user.userId, email: user.email, roles: user.roles, type: 'access' }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN, issuer: 'myapp.com', audience: 'myapp-api' } ); }

// Generate refresh token function generateRefreshToken(user: UserPayload): string { return jwt.sign( { sub: user.userId, type: 'refresh' }, JWT_SECRET, { expiresIn: REFRESH_TOKEN_EXPIRES_IN, issuer: 'myapp.com', audience: 'myapp-api' } ); }

// Verify and decode JWT async function verifyToken(token: string): Promise<any> { try { const decoded = await promisify(jwt.verify)(token, JWT_SECRET, { issuer: 'myapp.com', audience: 'myapp-api' }); return decoded; } catch (error) { if (error.name === 'TokenExpiredError') { throw new Error('Token expired'); } throw new Error('Invalid token'); } }

// Authentication middleware async function authenticateJWT(req, res, next) { const authHeader = req.headers.authorization;

if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'No token provided' }); }

const token = authHeader.substring(7);

try { const decoded = await verifyToken(token);

if (decoded.type !== 'access') {
  return res.status(401).json({ error: 'Invalid token type' });
}

req.user = decoded;
next();

} catch (error) { res.status(401).json({ error: error.message }); } }

JWT Best Practices

Use short expiration times: Access tokens should expire quickly (5-15 minutes) to limit exposure.

Store tokens securely:

  • Web: HttpOnly, Secure, SameSite cookies (protects against XSS)
  • Mobile: Secure storage (Keychain on iOS, Keystore on Android)
  • Never store in localStorage (vulnerable to XSS)

Include minimal claims: Only include necessary information in JWT payload to reduce token size.

Use strong signing algorithms: RS256 (RSA) or ES256 (ECDSA) for production, never HS256 with weak secrets.

Role-Based Access Control (RBAC)

RBAC assigns permissions to roles rather than individual users, simplifying authorization management.

Implementing RBAC

// Database schema (Prisma)
model User {
  id        String   @id @default(uuid())
  email     String   @unique
  roles     Role[]
}

model Role { id String @id @default(uuid()) name String @unique permissions Permission[] users User[] }

model Permission { id String @id @default(uuid()) resource String // e.g., 'posts', 'users', 'settings' action String // e.g., 'read', 'write', 'delete' roles Role[]

@@unique([resource, action]) }

// Authorization service class AuthorizationService { async hasPermission( userId: string, resource: string, action: string ): Promise<boolean> { const user = await prisma.user.findUnique({ where: { id: userId }, include: { roles: { include: { permissions: true } } } });

if (!user) return false;

// Check if any role grants the required permission
return user.roles.some(role =&gt;
  role.permissions.some(
    p =&gt; p.resource === resource &amp;&amp; p.action === action
  )
);

}

async hasRole(userId: string, roleName: string): Promise<boolean> { const user = await prisma.user.findUnique({ where: { id: userId }, include: { roles: true } });

return user?.roles.some(r =&gt; r.name === roleName) ?? false;

} }

// Authorization middleware function requirePermission(resource: string, action: string) { return async (req, res, next) => { const userId = req.user.sub;

const hasPermission = await authService.hasPermission(
  userId,
  resource,
  action
);

if (!hasPermission) {
  return res.status(403).json({
    error: 'Insufficient permissions',
    required: { resource, action }
  });
}

next();

}; }

// Usage in routes app.get('/api/posts', authenticateJWT, requirePermission('posts', 'read'), async (req, res) => { const posts = await prisma.post.findMany(); res.json(posts); } );

app.post('/api/posts', authenticateJWT, requirePermission('posts', 'write'), async (req, res) => { const post = await prisma.post.create({ data: req.body }); res.json(post); } );

app.delete('/api/posts/:id', authenticateJWT, requirePermission('posts', 'delete'), async (req, res) => { await prisma.post.delete({ where: { id: req.params.id } }); res.status(204).send(); } );

Common RBAC Roles

// Seed default roles and permissions
async function seedRoles() {
  // Create permissions
  const permissions = await Promise.all([
    prisma.permission.create({
      data: { resource: 'posts', action: 'read' }
    }),
    prisma.permission.create({
      data: { resource: 'posts', action: 'write' }
    }),
    prisma.permission.create({
      data: { resource: 'posts', action: 'delete' }
    }),
    prisma.permission.create({
      data: { resource: 'users', action: 'read' }
    }),
    prisma.permission.create({
      data: { resource: 'users', action: 'write' }
    }),
    prisma.permission.create({
      data: { resource: 'settings', action: 'write' }
    })
  ]);

// Create roles with associated permissions await prisma.role.create({ data: { name: 'admin', permissions: { connect: permissions.map(p => ({ id: p.id })) } } });

await prisma.role.create({ data: { name: 'editor', permissions: { connect: permissions .filter(p => p.resource === 'posts') .map(p => ({ id: p.id })) } } });

await prisma.role.create({ data: { name: 'viewer', permissions: { connect: permissions .filter(p => p.action === 'read') .map(p => ({ id: p.id })) } } }); }

Attribute-Based Access Control (ABAC)

ABAC provides fine-grained authorization based on attributes of the user, resource, and environment.

Implementing ABAC

interface AccessControlContext {
  user: {
    id: string;
    roles: string[];
    department: string;
    level: number;
  };
  resource: {
    type: string;
    id: string;
    ownerId: string;
    department: string;
    sensitivity: 'public' | 'internal' | 'confidential';
  };
  action: string;
  environment: {
    time: Date;
    ipAddress: string;
    userAgent: string;
  };
}

class ABACService { // Policy evaluation async evaluate(context: AccessControlContext): Promise<boolean> { const policies = await this.loadPolicies(context.resource.type);

for (const policy of policies) {
  if (this.evaluatePolicy(policy, context)) {
    return true;
  }
}

return false;

}

private evaluatePolicy(policy: Policy, context: AccessControlContext): boolean { // Evaluate all conditions in the policy return policy.conditions.every(condition => this.evaluateCondition(condition, context) ); }

private evaluateCondition(condition: Condition, context: AccessControlContext): boolean { const { attribute, operator, value } = condition; const actualValue = this.getAttribute(attribute, context);

switch (operator) {
  case 'equals':
    return actualValue === value;
  case 'in':
    return Array.isArray(value) &amp;&amp; value.includes(actualValue);
  case 'greaterThan':
    return actualValue &gt; value;
  case 'lessThan':
    return actualValue &lt; value;
  default:
    return false;
}

}

private getAttribute(attribute: string, context: AccessControlContext): any { const [category, field] = attribute.split('.');

switch (category) {
  case 'user':
    return context.user[field];
  case 'resource':
    return context.resource[field];
  case 'environment':
    return context.environment[field];
  default:
    return undefined;
}

} }

// Example policies const documentAccessPolicies = [ { name: 'Owner can always access', conditions: [ { attribute: 'user.id', operator: 'equals', value: 'resource.ownerId' } ] }, { name: 'Admins can access all documents', conditions: [ { attribute: 'user.roles', operator: 'in', value: ['admin'] } ] }, { name: 'Same department can access internal documents', conditions: [ { attribute: 'user.department', operator: 'equals', value: 'resource.department' }, { attribute: 'resource.sensitivity', operator: 'in', value: ['public', 'internal'] } ] }, { name: 'Level 3+ can access confidential documents in same department', conditions: [ { attribute: 'user.level', operator: 'greaterThan', value: 2 }, { attribute: 'user.department', operator: 'equals', value: 'resource.department' }, { attribute: 'resource.sensitivity', operator: 'equals', value: 'confidential' } ] } ];

// Middleware using ABAC function requireAccess(resourceType: string) { return async (req, res, next) => { const resource = await loadResource(resourceType, req.params.id);

const context: AccessControlContext = {
  user: {
    id: req.user.sub,
    roles: req.user.roles,
    department: req.user.department,
    level: req.user.level
  },
  resource: {
    type: resourceType,
    id: resource.id,
    ownerId: resource.ownerId,
    department: resource.department,
    sensitivity: resource.sensitivity
  },
  action: req.method,
  environment: {
    time: new Date(),
    ipAddress: req.ip,
    userAgent: req.get('user-agent')
  }
};

const hasAccess = await abacService.evaluate(context);

if (!hasAccess) {
  return res.status(403).json({ error: 'Access denied' });
}

next();

}; }

Session Management

Proper session management prevents session hijacking and fixation attacks.

Secure Session Implementation

import session from 'express-session';
import RedisStore from 'connect-redis';
import { createClient } from 'redis';

// Redis client for session storage const redisClient = createClient({ host: process.env.REDIS_HOST, port: parseInt(process.env.REDIS_PORT || '6379') });

app.use( session({ store: new RedisStore({ client: redisClient }), secret: process.env.SESSION_SECRET!, resave: false, saveUninitialized: false, name: 'sessionId', // Don't use default 'connect.sid' cookie: { secure: process.env.NODE_ENV === 'production', // HTTPS only httpOnly: true, // Prevents JavaScript access maxAge: 1000 * 60 * 60 * 24, // 24 hours sameSite: 'strict' // CSRF protection } }) );

// Regenerate session ID after login to prevent fixation app.post('/login', async (req, res) => { const { email, password } = req.body;

const user = await authenticateUser(email, password);

if (!user) { return res.status(401).json({ error: 'Invalid credentials' }); }

// Regenerate session ID req.session.regenerate((err) => { if (err) { return res.status(500).json({ error: 'Session error' }); }

req.session.userId = user.id;
req.session.loginTime = Date.now();

res.json({ success: true, user });

}); });

// Destroy session on logout app.post('/logout', (req, res) => { req.session.destroy((err) => { if (err) { return res.status(500).json({ error: 'Logout failed' }); }

res.clearCookie('sessionId');
res.json({ success: true });

}); });

// Session timeout middleware function checkSessionTimeout(req, res, next) { const SESSION_TIMEOUT = 1000 * 60 * 30; // 30 minutes

if (req.session.userId) { const inactiveTime = Date.now() - (req.session.lastActivity || req.session.loginTime);

if (inactiveTime &gt; SESSION_TIMEOUT) {
  req.session.destroy();
  return res.status(401).json({ error: 'Session expired' });
}

req.session.lastActivity = Date.now();

}

next(); }

Passwordless Authentication

Passwordless authentication eliminates password-related vulnerabilities while improving user experience.

Magic Link Authentication

import crypto from 'crypto';
import { sendEmail } from './email';

// Generate and store magic link token async function sendMagicLink(email: string): Promise<void> { const token = crypto.randomBytes(32).toString('hex'); const expires = new Date(Date.now() + 1000 * 60 * 15); // 15 minutes

// Store token in database await prisma.magicLinkToken.create({ data: { token, email, expires } });

const magicLink = ${process.env.APP_URL}/auth/verify?token=${token};

await sendEmail({ to: email, subject: 'Sign in to MyApp', html: &lt;p&gt;Click the link below to sign in:&lt;/p&gt; &lt;a href=&quot;${magicLink}&quot;&gt;${magicLink}&lt;/a&gt; &lt;p&gt;This link expires in 15 minutes.&lt;/p&gt; }); }

// Verify magic link token app.get('/auth/verify', async (req, res) => { const { token } = req.query;

const magicLink = await prisma.magicLinkToken.findUnique({ where: { token: token as string } });

if (!magicLink) { return res.status(400).send('Invalid token'); }

if (new Date() > magicLink.expires) { await prisma.magicLinkToken.delete({ where: { id: magicLink.id } }); return res.status(400).send('Token expired'); }

// Find or create user let user = await prisma.user.findUnique({ where: { email: magicLink.email } });

if (!user) { user = await prisma.user.create({ data: { email: magicLink.email } }); }

// Delete used token await prisma.magicLinkToken.delete({ where: { id: magicLink.id } });

// Create session req.session.regenerate((err) => { if (err) { return res.status(500).send('Authentication failed'); }

req.session.userId = user.id;
res.redirect('/dashboard');

}); });

WebAuthn (Passkeys)

import { generateRegistrationOptions, verifyRegistrationResponse } from '@simplewebauthn/server';

// Initiate passkey registration app.post('/auth/register/begin', async (req, res) => { const { email } = req.body;

const user = await prisma.user.findUnique({ where: { email } });

const options = await generateRegistrationOptions({ rpName: 'MyApp', rpID: 'myapp.com', userID: user.id, userName: user.email, attestationType: 'none', authenticatorSelection: { residentKey: 'preferred', userVerification: 'preferred' } });

// Store challenge for verification req.session.challenge = options.challenge;

res.json(options); });

// Complete passkey registration app.post('/auth/register/complete', async (req, res) => { const { credential } = req.body;

const verification = await verifyRegistrationResponse({ response: credential, expectedChallenge: req.session.challenge, expectedOrigin: 'https://myapp.com', expectedRPID: 'myapp.com' });

if (!verification.verified) { return res.status(400).json({ error: 'Verification failed' }); }

// Store credential for future authentication await prisma.passkey.create({ data: { userId: req.user.id, credentialId: verification.registrationInfo.credentialID, publicKey: verification.registrationInfo.credentialPublicKey, counter: verification.registrationInfo.counter } });

res.json({ success: true }); });

Multi-Factor Authentication (MFA)

MFA adds an extra layer of security beyond passwords.

Time-Based One-Time Passwords (TOTP)

import speakeasy from 'speakeasy';
import QRCode from 'qrcode';

// Enable TOTP MFA app.post('/auth/mfa/enable', authenticateJWT, async (req, res) => { const secret = speakeasy.generateSecret({ name: MyApp (${req.user.email}), issuer: 'MyApp' });

// Generate QR code for authenticator app const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);

// Store secret temporarily until verified req.session.pendingMfaSecret = secret.base32;

res.json({ qrCode: qrCodeUrl, secret: secret.base32 }); });

// Verify and activate TOTP app.post('/auth/mfa/verify', authenticateJWT, async (req, res) => { const { token } = req.body; const secret = req.session.pendingMfaSecret;

const verified = speakeasy.totp.verify({ secret, encoding: 'base32', token, window: 2 // Allow 2 time steps for clock drift });

if (!verified) { return res.status(400).json({ error: 'Invalid token' }); }

// Save MFA secret to user await prisma.user.update({ where: { id: req.user.sub }, data: { mfaSecret: secret, mfaEnabled: true } });

delete req.session.pendingMfaSecret;

res.json({ success: true }); });

// Verify TOTP during login app.post('/auth/login', async (req, res) => { const { email, password, totpToken } = req.body;

const user = await authenticateUser(email, password);

if (!user) { return res.status(401).json({ error: 'Invalid credentials' }); }

// Check if MFA is enabled if (user.mfaEnabled) { if (!totpToken) { return res.status(200).json({ mfaRequired: true }); }

const verified = speakeasy.totp.verify({
  secret: user.mfaSecret,
  encoding: 'base32',
  token: totpToken,
  window: 2
});

if (!verified) {
  return res.status(401).json({ error: 'Invalid MFA token' });
}

}

// Create session const accessToken = generateAccessToken(user); const refreshToken = generateRefreshToken(user);

res.json({ accessToken, refreshToken }); });

API Key Authentication

API keys provide simple authentication for service-to-service communication.

Implementing API Keys

import crypto from 'crypto';

// Generate API key app.post('/api/keys', authenticateJWT, async (req, res) => { const { name, scopes } = req.body;

// Generate cryptographically secure key const key = sk_${crypto.randomBytes(32).toString('hex')}; const hashedKey = await hashApiKey(key);

const apiKey = await prisma.apiKey.create({ data: { name, keyHash: hashedKey, userId: req.user.sub, scopes: scopes || [] } });

// Return key only once - cannot be retrieved later res.json({ id: apiKey.id, key, // Show only on creation name: apiKey.name, scopes: apiKey.scopes }); });

async function hashApiKey(key: string): Promise<string> { return crypto .createHash('sha256') .update(key) .digest('hex'); }

// API key authentication middleware async function authenticateApiKey(req, res, next) { const apiKey = req.headers['x-api-key'];

if (!apiKey || typeof apiKey !== 'string') { return res.status(401).json({ error: 'API key required' }); }

const hashedKey = await hashApiKey(apiKey);

const key = await prisma.apiKey.findUnique({ where: { keyHash: hashedKey }, include: { user: true } });

if (!key) { return res.status(401).json({ error: 'Invalid API key' }); }

if (!key.active) { return res.status(401).json({ error: 'API key revoked' }); }

// Update last used timestamp await prisma.apiKey.update({ where: { id: key.id }, data: { lastUsedAt: new Date() } });

req.user = key.user; req.apiKeyScopes = key.scopes;

next(); }

// Scope validation middleware function requireScope(scope: string) { return (req, res, next) => { if (!req.apiKeyScopes || !req.apiKeyScopes.includes(scope)) { return res.status(403).json({ error: 'Insufficient scope', required: scope }); } next(); }; }

// Usage app.get('/api/data', authenticateApiKey, requireScope('data:read'), async (req, res) => { // Handle request } );

Security Best Practices

Rate Limiting

import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';

// Authentication endpoint rate limiting const authLimiter = rateLimit({ store: new RedisStore({ client: redisClient, prefix: 'rl:auth:' }), windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 attempts per window message: 'Too many login attempts, please try again later', standardHeaders: true, legacyHeaders: false });

app.post('/auth/login', authLimiter, async (req, res) => { // Handle login });

// API rate limiting const apiLimiter = rateLimit({ store: new RedisStore({ client: redisClient, prefix: 'rl:api:' }), windowMs: 60 * 1000, // 1 minute max: 100, // 100 requests per minute keyGenerator: (req) => { // Rate limit by API key or user ID return req.apiKey?.id || req.user?.sub || req.ip; } });

app.use('/api', apiLimiter);

Password Hashing

import bcrypt from 'bcrypt';

const SALT_ROUNDS = 12;

async function hashPassword(password: string): Promise<string> { return bcrypt.hash(password, SALT_ROUNDS); }

async function verifyPassword(password: string, hash: string): Promise<boolean> { return bcrypt.compare(password, hash); }

// Password strength validation function isStrongPassword(password: string): boolean { const minLength = 12; const hasUpperCase = /[A-Z]/.test(password); const hasLowerCase = /[a-z]/.test(password); const hasNumbers = /\d/.test(password); const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);

return ( password.length >= minLength && hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar ); }

Token Blacklisting

// Blacklist tokens on logout or password change
async function blacklistToken(token: string): Promise<void> {
  const decoded = jwt.decode(token) as any;
  const expiresIn = decoded.exp - Math.floor(Date.now() / 1000);

// Store in Redis with TTL matching token expiration await redisClient.setEx( blacklist:${token}, expiresIn, 'revoked' ); }

// Check blacklist in authentication middleware async function authenticateJWT(req, res, next) { const token = extractToken(req);

// Check if token is blacklisted const isBlacklisted = await redisClient.exists(blacklist:${token}); if (isBlacklisted) { return res.status(401).json({ error: 'Token revoked' }); }

// Verify token try { const decoded = await verifyToken(token); req.user = decoded; next(); } catch (error) { res.status(401).json({ error: error.message }); } }

Real-World Examples

GitHub's Authentication

GitHub uses multiple authentication methods:

  • OAuth Apps: For third-party integrations
  • Personal Access Tokens: Fine-grained permissions with 1-year expiration
  • SSH Keys: For Git operations
  • SAML SSO: For enterprise organizations

Their OAuth implementation supports the authorization code flow with PKCE and enforces short-lived access tokens (8 hours) with refresh tokens for extended access.

Auth0's Platform

Auth0 provides authentication as a service handling:

  • Universal Login: Centralized login page preventing phishing
  • Anomaly Detection: Identifies suspicious login patterns (impossible travel, brute force)
  • Adaptive MFA: Requires additional authentication for risky logins
  • Session Management: Configurable timeouts and concurrent session limits

Auth0 processes over 2.5 billion logins monthly with 99.99% uptime SLA.

Stripe's API Authentication

Stripe uses API keys with distinct prefixes:

  • pk_: Publishable keys (client-side, public)
  • sk_: Secret keys (server-side only)
  • rk_: Restricted keys (limited permissions)

Keys are scoped to test or live environments, preventing accidental production charges during development. Stripe also implements automatic key rotation and provides detailed audit logs of API key usage.

Conclusion

Authentication and authorization form the security foundation of modern applications. OAuth 2.0 and OIDC provide industry-standard authentication flows, while JWTs enable stateless authorization. Implement RBAC for simple permission models or ABAC for fine-grained control based on attributes. Enhance security with MFA, passwordless authentication, and proper session management.

Following these production patterns - along with rate limiting, secure password hashing, and token management - creates a robust authentication system that protects user data while maintaining excellent user experience. Regular security audits and staying updated on emerging threats ensure your authentication implementation remains secure over time.

Found this helpful? Share it!

Related Articles

S

Written by StaticBlock Editorial

StaticBlock Editorial is a technical writer and software engineer specializing in web development, performance optimization, and developer tooling.