Authentication Patterns in Modern Web Apps: JWT, OAuth, and Session Management | SoniNow Blog

Limited TimeLearn More

authenticationjwtoauthsessionssecurity

Authentication Patterns in Modern Web Apps: JWT, OAuth, and Session Management

Published

2026-06-23

Read Time

4 mins

Authentication Patterns in Modern Web Apps: JWT, OAuth, and Session Management

Authentication is the first line of defense for any web application. Choosing the right pattern — and implementing it correctly — determines whether your users' identities remain protected or become the weakest link in your security posture.

JWT Implementation: Stateless Tokens Done Right

JSON Web Tokens enable stateless authentication where the server does not need to store session data. A correctly implemented JWT flow uses short-lived access tokens (15 minutes), refresh tokens stored in server-side rotation, and proper signature verification.

import jwt from 'jsonwebtoken';

function generateTokens(userId: string) {
  const accessToken = jwt.sign(
    { sub: userId, role: 'user' },
    process.env.JWT_ACCESS_SECRET!,
    { expiresIn: '15m', algorithm: 'RS256' }
  );

  const refreshToken = jwt.sign(
    { sub: userId, type: 'refresh' },
    process.env.JWT_REFRESH_SECRET!,
    { expiresIn: '7d', algorithm: 'RS256' }
  );

  return { accessToken, refreshToken };
}

The key decisions are the signing algorithm (prefer RS256 or ES256 over HS256), the claims included (minimal — never include sensitive data), and the expiration policy. Store access tokens in memory for single-page applications and in Authorization headers for APIs. Never store access tokens in localStorage where XSS can exfiltrate them.

OAuth 2.0 Flows for Different Application Types

OAuth 2.0 is not a single flow but a family of authorization grants. The Authorization Code flow with PKCE is the standard for single-page applications and mobile apps. The Client Credentials flow serves server-to-server communication. The authorization code flow (without PKCE) remains appropriate for traditional server-rendered applications.

// PKCE code challenge generation
async function generatePKCE() {
  const codeVerifier = crypto.randomBytes(32).toString('base64url');
  const codeChallenge = crypto
    .createHash('sha256')
    .update(codeVerifier)
    .digest('base64url');

  return { codeVerifier, codeChallenge };
}

PKCE prevents authorization code interception attacks by binding the authorization request to the token exchange through a cryptographic verifier. Every OAuth implementation should also validate the redirect_uri against a configured allowlist and never accept user-supplied redirect targets.

Session Management: Server-Side State

Server-side sessions remain the best choice for applications requiring real-time revocation capabilities. When a user changes their password, their sessions should be invalidated immediately. This is trivial with server-side session stores and complex with stateless JWTs.

// Session configuration with Redis store
import session from 'express-session';
import RedisStore from 'connect-redis';
import { createClient } from 'redis';

const redisClient = createClient({ url: process.env.REDIS_URL });

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET!,
  name: 'sessionId',
  cookie: {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    maxAge: 24 * 60 * 60 * 1000, // 24 hours
  },
  resave: false,
  saveUninitialized: false,
}));

Session identifiers should be cryptographically random, never appear in URLs, and be rotated upon login and privilege escalation. The session store itself must be hardened — if Redis, configure authentication, TLS, and a dedicated Redis instance for session data with proper eviction policies.

Refresh Token Rotation and Reuse Detection

Refresh token rotation invalidates a refresh token each time it is used and issues a new one. If a compromised refresh token is used by an attacker and then the legitimate user also tries to use their (now invalidated) token, the server detects the reuse and can revoke all tokens for that user.

async function rotateRefreshToken(oldToken: string) {
  const token = await TokenModel.findOne({ value: hash(oldToken) });
  if (!token) {
    // Token reuse detected — revoke all user tokens
    await TokenModel.deleteMany({ userId: token.userId });
    throw new Error('Potential token theft detected');
  }
  // Issue new token, invalidate old
  const newToken = generateRefreshToken();
  await TokenModel.updateOne(
    { _id: token._id },
    { value: hash(newToken), rotatedAt: new Date() }
  );
  return newToken;
}

Multi-Factor Authentication Integration

Password-based authentication alone is insufficient for any application handling sensitive data. Time-based One-Time Passwords (TOTP), WebAuthn (passkeys), and hardware security keys provide layered security. WebAuthn is increasingly the preferred second factor because it is phishing-resistant and built into modern browsers.

Secure Token Storage in the Browser

For browser-based applications, the storage location of tokens directly impacts security. Memory variables are safest for SPAs but require re-authentication on page refresh. HTTP-only cookies mitigate XSS exposure but are vulnerable to CSRF. A combined approach — short-lived access tokens in memory with refresh tokens in HTTP-only cookies — balances security and usability.

Handling Token Expiration Gracefully

Silent token refresh in the background prevents disruptive mid-session logouts. Axios interceptors or fetch wrappers can detect 401 responses and attempt a refresh before retrying the original request. Implement exponential backoff for refresh failures to avoid thundering herd problems when multiple requests fail simultaneously.

Implementing authentication correctly requires understanding the trade-offs between security and user experience. Our <a href="/services/web-development">web development team</a> specializes in building authentication systems that protect user data without compromising usability. Reach out to discuss your authentication architecture.