1. Authentication vs. Authorization: Untangling the Core Confusion
A persistent source of architectural vulnerability in modern application engineering is the conflation of authentication (AuthN) and authorization (AuthZ).
- OAuth 2.0 (RFC 6749) is strictly an authorization framework. It specifies protocols for a resource owner to delegate access to their protected resources to a third-party application without sharing their credentials. OAuth 2.0 issues Access Tokens. OAuth 2.0 has no concept of who the user is, how they authenticated, or when they logged in.
- OpenID Connect (OIDC) is an identity layer built on top of OAuth 2.0. It standardizes authentication by introducing an ID Token (a signed JSON Web Token describing the authenticated subject) and a standardized
userinfoendpoint.
Using an OAuth 2.0 access token as proof of authentication—or trusting user identity simply because a client received a bearer token—creates serious authentication bypass vulnerabilities.
+--------------------------------------------------------------------------+
| Identity & Access Topology |
+------------------------------------+-------------------------------------+
|
+---------------------+---------------------+
| |
v v
OAuth 2.0 (Authorization) OpenID Connect (Authentication)
- Protocol: RFC 6749 / RFC 8414 - Specification: OIDC Core 1.0
- Artifact: Access Token - Artifact: ID Token (JWT)
- Audience: Resource Server (API) - Audience: Client Application (RP)
- Question: "What is client allowed to do?" - Question: "Who is the user?"
2. The Four OAuth 2.0 Roles
- Resource Owner: The entity capable of granting access to a protected resource (typically the human end-user).
- Client: The application requesting access to protected resources on behalf of the resource owner (Single Page App, mobile app, or backend microservice).
- Authorization Server: The system issuing tokens to clients after successfully authenticating the resource owner and obtaining consent (e.g., Auth0, Okta, Keycloak, AWS Cognito).
- Resource Server: The HTTP server hosting protected resources, capable of accepting and validating incoming access tokens (your backend API gateway or microservice).
3. The Standard: Authorization Code Flow with PKCE
Under modern security specifications (including the OAuth 2.0 Security Best Current Practice RFC 9700 and the OAuth 2.1 specification draft), the legacy Implicit Grant is officially deprecated and forbidden.
The gold standard for both confidential clients (server-side web apps) and public clients (Single Page Apps and mobile native apps) is the Authorization Code Flow with Proof Key for Code Exchange (PKCE, RFC 7636).
Client App (Browser) Authorization Server Backend API (Resource Server)
| | |
| 1. Generate code_verifier | |
| Calculate code_challenge (S256)| |
| 2. GET /authorize with challenge | |
|---------------------------------->| |
| | |
| 3. User authenticates & consents | |
| 4. Redirect with auth_code | |
|<----------------------------------| |
| | |
| 5. POST /token + code_verifier | |
|---------------------------------->| |
| 6. Validate verifier vs challenge | |
| Issue ID & Access Tokens | |
|<----------------------------------| |
| |
| 7. Request protected data (Bearer Access Token) |
|---------------------------------------------------------------------->|
| 8. Verify token signature via JWKS & process request |
|<----------------------------------------------------------------------|
The Cryptographic Mechanics of PKCE
- The client generates a high-entropy cryptographically random string:
code_verifier(between 43 and 128 characters, unreserved URL characters). - The client calculates the SHA-256 hash and Base64URL-encodes it: $$\text{code_challenge} = \text{BASE64URL-ENCODE}(\text{SHA256}(\text{code_verifier}))$$
- The authorization request includes
code_challengeandcode_challenge_method=S256, along with a randomstateparameter for CSRF mitigation and anonceparameter for replay protection. - When exchanging the temporary authorization code, the client sends the raw
code_verifier. Even if an attacker intercepts the authorization code via redirect hijacking, they cannot redeem it without the originalcode_verifier.
4. Secure Validation of ID Tokens and Access Tokens
When a Resource Server receives an incoming HTTP request containing an Authorization: Bearer <token> header, it must perform rigorous cryptographic and semantic validation before processing data.
Validation Sequence
import { jwtVerify, createRemoteJWKSet } from 'jose';
// Fetch public signing keys from identity provider's JWKS endpoint
const JWKS = createRemoteJWKSet(
new URL('https://auth.ladestack.in/.well-known/jwks.json')
);
export async function validateAccessToken(bearerHeader: string | undefined) {
if (!bearerHeader?.startsWith('Bearer ')) {
throw new Error('Missing or invalid Authorization header');
}
const token = bearerHeader.split(' ')[1];
// Cryptographically verify signature and standard claims
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://auth.ladestack.in/',
audience: 'https://api.ladestack.in',
algorithms: ['RS256', 'ES256'], // Explicitly whitelist asymmetric algorithms; forbid 'none' or 'HS256'
});
// Verify token expiry and active scope
const scopes = (payload.scope as string)?.split(' ') || [];
if (!scopes.includes('read:telemetry')) {
throw new Error('Insufficient scope permissions');
}
return {
subject: payload.sub,
tenantId: payload['https://ladestack.in/tenant_id'],
scopes,
};
}
Essential JWT Security Rules
- Reject
alg: "none": Ensure your JWT library disallows unsigned tokens. - Algorithm Confusion Attacks: When using asymmetric signing (RS256), ensure the verification library does not accept symmetric verification (HS256) where an attacker signs a forgery using the server’s public key as an HMAC secret.
- Audience & Issuer Validation: Always validate that
audmatches your specific API identifier andissmatches your identity provider domain.
5. Token Storage Architecture: The Backend-for-Frontend (BFF) Pattern
Where should tokens live in single-page browser applications?
The Vulnerability of Browser Storage
Storing access tokens and refresh tokens in localStorage or sessionStorage exposes them directly to Cross-Site Scripting (XSS). Any compromised third-party script, malicious NPM dependency, or CDN injection can read storage and exfiltrate credentials instantly.
The BFF Architecture
To eliminate token exposure in the browser, modern enterprise architectures implement the Backend-for-Frontend (BFF) pattern:
Browser (SPA) BFF Proxy Node / Gateway IdP / Microservices
| | |
| GET /api/data | |
| Cookie: __Host-SessionId=xxx | |
|---------------------------------->| 1. Read encrypted server session |
| | 2. Extract raw Access Token |
| | 3. Forward request with Bearer |
| |---------------------------------->|
| |<----------------------------------|
|<----------------------------------| |
- The browser application never touches raw OAuth tokens.
- The authorization exchange occurs securely between the BFF and the Authorization Server.
- The BFF establishes a secure HTTP-only session with the client using cookies flagged with
HttpOnly; Secure; SameSite=Strict; Path=/. - The BFF attaches the raw bearer token when proxying calls to downstream APIs.
6. Refresh Token Rotation and Revocation
Refresh tokens grant long-lived access. If stolen, attackers maintain persistent access.
Refresh Token Rotation (RTR) mandates that whenever a refresh token is exchanged for a new access token, the Authorization Server invalidates the old refresh token and issues a single new one.
Client Authorization Server
| |
| POST /token (RefreshToken_A) |
|----------------------------------------->|
| | Invalidate RefreshToken_A
| | Issue AccessToken_2 + RefreshToken_B
|<-----------------------------------------|
| |
| (Attacker attempts reuse of Token_A) |
| POST /token (RefreshToken_A) |
|----------------------------------------->|
| | ALARM: Detected Reuse!
| | Instantly revoke Token_B & invalidate
| | entire user session family.
|<-----------------------------------------|
If an invalidated refresh token is ever submitted, the server detects token theft and immediately revokes all tokens within that session family, forcing a full re-authentication.
Production OAuth 2.0 / OIDC Verification Checklist
- Authorization Code Flow with PKCE (
S256) enforced for all clients. - Implicit grant and Resource Owner Password Credentials grant disabled.
- Redirect URIs enforced via exact string matching; no wildcard domains.
- High-entropy
stateparameter validated to prevent CSRF during authorization. -
nonceverified in ID Token to prevent token replay attacks. - Access tokens validated against remote JWKS with strict
iss,aud, andexpchecks. - Tokens stored in
HttpOnly; Secure; SameSite=Strictcookies via BFF architecture. - Refresh Token Rotation enabled with automatic reuse detection and revocation.
- Standard RP-initiated logout clears both local application sessions and IdP SSO cookies.