Identity & OTP Services
Prerequisite:
Overview
Authentication is the process of proving you are who you claim to be. Identity systems answer the question “who is this user?” and authentication protocols establish that proof in a way that is cryptographically verifiable and resistant to replay attacks. Getting this wrong has severe consequences - credential theft, account takeover, and data breaches are all rooted in weak or poorly implemented authentication.
This post covers the core concepts: federated identity via OAuth 2.0 and OIDC, JSON Web Tokens, one-time passwords including TOTP and its cryptographic foundation, and the emerging standard of passkeys.
Federated Identity: OAuth 2.0 and OIDC
OAuth 2.0
OAuth 2.0 is an authorization framework that allows a user to grant a third-party application access to resources on another service without sharing their password. The protocol has four roles:
- Resource Owner: the user
- Client: the application requesting access (e.g., a mobile app)
- Authorization Server: issues tokens after authenticating the user (e.g., Google’s auth server)
- Resource Server: the API that holds the protected resources
The client redirects the user to the authorization server, which authenticates them and returns an authorization code. The client exchanges this code for an access token, which it presents to the resource server. OAuth 2.0 handles authorization, not authentication - it tells you what a token grants access to, not who the user is.
OpenID Connect (OIDC)
OIDC is a thin identity layer on top of OAuth 2.0 that adds authentication. It introduces the ID token - a JWT containing identity claims like sub (subject/user ID), email, name, and iss (issuer). The ID token lets the client verify who the user is, not just what they can access. “Sign in with Google” and “Sign in with GitHub” flows are built on OIDC.
JSON Web Tokens
A JWT (JSON Web Token) has three base64url-encoded parts separated by dots: header.payload.signature.
- Header: specifies the algorithm (e.g.,
RS256for RSA + SHA-256) - Payload: contains claims -
sub(subject),iss(issuer),exp(expiration),iat(issued at), plus any custom claims - Signature: signs header + payload with the server’s secret or private key, preventing tampering
JWTs are stateless: the server can verify a token without a database lookup by re-computing the signature. This makes them scalable but introduces a challenge: you cannot revoke a JWT before it expires without maintaining a blocklist, which reintroduces state.
Access Tokens vs Refresh Tokens
A common pattern uses short-lived access tokens (15 minutes) and long-lived refresh tokens (days or weeks). The access token is passed on every API request. When it expires, the client silently exchanges the refresh token for a new access token pair. Refresh tokens are stored securely (HttpOnly cookies or secure storage) and can be revoked server-side, giving you a meaningful revocation mechanism.
One-Time Passwords
TOTP: Time-Based One-Time Passwords
TOTP (RFC 6238) generates a 6-digit code that changes every 30 seconds. The algorithm is:
$$TOTP(K, T) = HOTP(K, \lfloor T / T_0 \rfloor)$$
where $K$ is a shared secret, $T$ is the current Unix time, and $T_0$ is the time step (30 seconds). HOTP (RFC 4226) computes an HMAC-SHA1 over the key and counter, then extracts 6 digits via dynamic truncation.
Both the authenticator app and the server share the secret $K$ (shared once during setup via a QR code). Since both compute the same HOTP over the same time window, they produce identical codes without network communication. The server accepts codes from a small window (e.g., ±1 time step) to account for clock drift.
Authenticator apps like Google Authenticator and Authy implement TOTP. The codes are valid only once per window, making them resistant to replay attacks.
SMS OTP and Its Pitfalls
SMS one-time passwords are widely used but considered weak. The primary vulnerability is SIM swapping: an attacker socially engineers a mobile carrier into transferring your phone number to their SIM, gaining access to your SMS codes. SMS OTP is better than no second factor, but TOTP or hardware keys are strongly preferred for sensitive systems.
WebAuthn and Passkeys
WebAuthn (Web Authentication API) is a W3C standard that enables authentication using public-key cryptography. During registration, the authenticator (a device or hardware key) generates a public-private key pair; the public key is stored on the server. At login, the server sends a challenge; the authenticator signs it with the private key. The private key never leaves the device.
Passkeys are a user-friendly implementation of WebAuthn synced across devices via platform providers (iCloud Keychain, Google Password Manager). They are phishing-resistant because the key pair is scoped to the exact origin - a fake site cannot trick a passkey into signing a challenge.
Session Management
After authentication, the server must maintain session state. Browser sessions typically use cookies with:
- HttpOnly: prevents JavaScript from reading the cookie, mitigating XSS token theft
- Secure: only sent over HTTPS
- SameSite=Strict or Lax: mitigates CSRF attacks by restricting cross-site cookie sending
For mobile and API clients, JWTs in Authorization headers are the standard approach. Either way, you need a logout/revocation mechanism - for JWTs this means a token blocklist or short expiry; for sessions it means server-side session deletion.
Examples
OAuth2 PKCE flow for mobile apps. Mobile apps cannot safely store a client secret, so they use PKCE (Proof Key for Code Exchange). The client generates a random code_verifier, hashes it to a code_challenge, and sends the challenge with the authorization request. When exchanging the authorization code for tokens, the client sends the original code_verifier - the server verifies the hash matches, ensuring only the original requestor can exchange the code.
TOTP implementation sketch. Generate a random 160-bit secret, base32-encode it for display, and encode it in a otpauth:// URI for QR generation. During verification, compute TOTP for the current window and the adjacent windows, compare against the user-submitted code with a constant-time comparison to prevent timing attacks, and mark the code as used to prevent immediate reuse.
JWT validation pitfalls. Always validate the alg header against a server-side allowlist - the “none” algorithm attack exploits libraries that accept unsigned tokens if alg: none is set. Validate exp, iss, and aud claims. Use a library that handles these checks rather than parsing JWTs manually.
Read Next: