Authentication is one of those decisions that feels simple at the start and becomes incredibly consequential six months later. Pick the wrong approach and you end up with security vulnerabilities, scaling problems, or an authentication system so complex that every new feature requires twice the engineering time. The choice between JWT, sessions, and OAuth is not about which technology is "best." It is about which approach fits your application's actual requirements.
We have built authentication systems across dozens of projects, from single page applications to multi tenant SaaS platforms to mobile apps with offline support. The patterns that work well in one context fail completely in another. This guide breaks down when each approach shines and when it creates problems you will regret.
Session Based Authentication: The Reliable Default
Session based auth is the oldest pattern and still the most straightforward. The server creates a session after login, stores it (typically in a database or Redis), and sends the client a session ID in a cookie. Every subsequent request includes that cookie, the server looks up the session, and knows who the user is.
When sessions work well:
- Server rendered applications. If your application primarily renders HTML on the server, sessions are the natural fit. The cookie flows automatically with every request. No client side JavaScript needed for auth.
- Applications requiring immediate revocation. When a user changes their password, gets banned, or an admin needs to force logout, you delete the session from the store. Done. The next request fails authentication immediately. No waiting for token expiry.
- Single domain applications. When your frontend and backend share a domain, cookies work seamlessly with proper `SameSite`, `HttpOnly`, and `Secure` flags.
Where sessions create friction:
- Cross domain architectures. If your API serves multiple frontends on different domains, cookie based sessions get complicated fast. CORS configuration, third party cookie restrictions, and varying browser policies make this painful.
- Horizontal scaling. Every request requires a session lookup. With a single server this is trivial. With 20 servers behind a load balancer, you need sticky sessions or a shared session store. Redis handles this well, but it is another piece of infrastructure to manage.
- Mobile applications. Native mobile apps do not handle cookies the same way browsers do. You can make it work, but it is not the natural pattern.
We covered session security in depth in our web app security checklist. If you go the session route, that checklist is essential reading.
JWT Authentication: Power and Responsibility
JSON Web Tokens took over the industry around 2015 and for good reason. A JWT is a self contained token that includes the user's identity and claims, signed cryptographically by the server. The key architectural advantage: the server does not need to store anything. It verifies the token's signature and trusts the claims inside.
When JWTs work well:
- Distributed systems and microservices. When requests flow through multiple services, each service can verify the JWT independently without calling a central auth service. This is a genuine architectural win at scale. The token carries the user's identity and permissions everywhere it goes.
- Cross domain and cross platform. JWTs live in the `Authorization` header, not in cookies. This works identically across web, mobile, and API consumers. If you are building an API that serves a web app, a mobile app, and third party integrations, JWTs simplify authentication significantly.
- Serverless and edge computing. Stateless verification means your auth check runs at the edge without any database call. For serverless architectures, this reduces latency and eliminates a scaling bottleneck.
Where JWTs create problems:
- Token revocation. This is the big one. Once you issue a JWT with a 1 hour expiry, that token is valid for 1 hour no matter what. User got hacked? Token is still valid. Admin banned the account? Token is still valid. The workarounds (short expiry with refresh tokens, token blacklists) add complexity that partially negates the "stateless" benefit.
- Token size. JWTs are larger than session IDs. A typical JWT with user claims is 800 to 1200 bytes. Multiply that by every API request and it adds up, especially on mobile networks. Pack too many claims into the token and you are sending kilobytes of data with every request.
- Storage on the client. Where do you put the JWT in a browser? `localStorage` is vulnerable to XSS. Cookies work but then you are back to cookie management. `sessionStorage` does not persist across tabs. Each option has tradeoffs, and the wrong choice creates real security vulnerabilities.
Our recommendation for JWT implementations: Use short lived access tokens (5 to 15 minutes) with longer lived refresh tokens stored in `HttpOnly` cookies. The access token goes in the `Authorization` header for API calls. The refresh token stays in a secure cookie and is only sent to your token refresh endpoint. This gives you the stateless benefits of JWT for most requests while maintaining a revocation path through the refresh token.
OAuth 2.0 and OpenID Connect: Delegated Identity
OAuth is fundamentally different from sessions and JWTs. It is not an authentication mechanism itself. It is a delegation framework that lets users authenticate through a third party (Google, GitHub, Apple) and grants your application limited access to their identity.
When OAuth is essential:
- Social login. If users expect to sign in with Google or Apple, you need OAuth. Period. Building this correctly matters enormously for web and mobile applications where reducing signup friction directly impacts conversion.
- Third party API access. If your application needs to read a user's Google Calendar, post to their Slack workspace, or access their GitHub repositories, OAuth is how you get authorized access without handling their credentials.
- Enterprise SSO. When selling to companies that require SAML or OIDC integration with their identity provider (Okta, Azure AD, etc.), OAuth/OIDC is the standard protocol.
What OAuth does not solve:
OAuth tells you who the user is. It does not tell you what they can do in your application. You still need an internal authorization system, role assignments, permission checks, and access control logic. Many teams conflate authentication and authorization, implement OAuth, and then realize they still need to build all the access control from scratch.
The practical implementation: Use OAuth for identity verification, then issue your own session or JWT for subsequent requests. Do not pass the OAuth provider's tokens around your system. Exchange them at the boundary for your own tokens that contain your application's claims and permissions.
The Decision Framework
Here is the framework we use when designing system architecture for client projects:
Choose sessions when:
- You have a server rendered application on a single domain
- Immediate session revocation is a hard requirement
- Your team is small and you want minimal auth complexity
- You are not serving mobile clients or third party API consumers
Choose JWTs when:
- You have multiple clients (web, mobile, API) hitting the same backend
- You run a microservices or distributed architecture
- You need stateless verification at the edge or in serverless functions
- You can accept the complexity of refresh token rotation
Choose OAuth/OIDC when:
- Users expect social login
- You need to access third party APIs on behalf of users
- Enterprise customers require SSO integration
- You want to offload identity management to a dedicated provider
The common combination: Most production applications we build use all three. OAuth handles the identity provider integration. The server issues JWTs for API access. Sessions (via refresh tokens in cookies) maintain the long lived authentication state. Understanding how these layers compose is more important than choosing one over another.
Security Considerations That Matter
Regardless of which approach you choose, these security fundamentals apply:
Always use HTTPS. Tokens and session IDs sent over HTTP are trivially interceptable. There is no excuse for this in 2026.
Implement CSRF protection for cookie based auth. If you use cookies (sessions or refresh tokens), you need `SameSite` flags and CSRF tokens. The OWASP guidelines are your reference.
Validate on the server, always. Client side token validation is for UX (showing/hiding UI elements). Server side validation is for security. Never trust a token claim without verifying the signature server side.
Rotate secrets and keys. JWT signing keys, session secrets, and OAuth client secrets should rotate on a schedule. Design your system to support key rotation from the start because retrofitting it is painful.
Log authentication events. Failed logins, token refreshes, password changes, and permission changes should all generate audit logs. When a security incident happens, and eventually one will, these logs are how you understand the scope. This ties into the broader observability practices we described in our API design guide.
Do Not Build Auth From Scratch (Unless You Must)
For most applications, we recommend using a managed auth provider like Supabase Auth, Auth0, or Firebase Auth for the identity layer. These providers handle password hashing, token management, social login integration, and security updates. They have dedicated security teams finding and patching vulnerabilities that your team would never catch.
Build custom auth only when you have specific requirements that managed providers cannot meet: unusual token claims, custom MFA flows, or regulatory requirements that mandate full control of the authentication pipeline. Even then, use established libraries (like `jose` for JWT handling or `passport` for Node.js OAuth) rather than implementing cryptographic operations yourself.
If you are designing or redesigning your authentication architecture and want to get it right the first time, contact our team. We will help you choose the right approach for your specific requirements and build it to production security standards.