ProCust
Authentication Done Right: OAuth, JWT or Sessions?

Authentication Done Right: OAuth, JWT or Sessions?

Sessions, JWT or OAuth with OpenID Connect: when each authentication method fits, and the common mistakes that leave web applications open to attack.

In short: For most web applications, a classic server-side session with a secure, httpOnly cookie is the simplest and safest form of authentication. JWTs make sense when several services or mobile apps need to verify tokens without a central lookup, but they make revocation harder. OAuth 2.0 with OpenID Connect is the standard once users should sign in with Google, Microsoft or a central identity provider. Many projects are best served by a proven auth solution instead of building their own.

Authentication versus authorisation

Authentication answers who someone is. Authorisation decides what they may do. The two often get mixed up, but they belong in different layers. Logging in with a password and a second factor is authentication. Making sure a clerk only sees orders from her own branch is authorisation, and it has to be checked on the server for every request, not only in the UI.

Sessions: the underrated default

With a server-side session, the browser receives a random session ID in a cookie after login. The actual session data lives on the server or in a store like Redis. The advantage is that you can end a session instantly: on logout, after a password change, or when an account is locked.

Set the cookie with HttpOnly so JavaScript cannot read it, Secure so it only travels over HTTPS, and SameSite=Lax or Strict as protection against CSRF. Issue a fresh session ID after login to prevent session fixation. For a classic web app with its own backend, this is usually the best choice.

JWT: when tokens make sense

A JSON Web Token is a signed payload carrying information such as the user ID and roles. Any service with the right key can verify the signature without asking a database. That is handy for distributed systems, APIs serving mobile apps, or several backends sharing one identity.

The downside is that an issued token stays valid until it expires. The usual answer is short-lived access tokens of a few minutes plus longer-lived refresh tokens that are replaced on every use (rotation). If an old refresh token shows up again, that signals theft, and the whole token family is revoked.

Common mistakes include storing tokens in localStorage, where any injected script can read them, not pinning the signature algorithm, and putting too much data in the token. A JWT is signed, not encrypted, so anyone who intercepts it can read its contents. Our web security fundamentals cover these attacks in more detail.

OAuth 2.0 and OpenID Connect

OAuth 2.0 is a protocol for delegating access: an application may call an API on a user's behalf without knowing their password. Login itself is handled by OpenID Connect, a layer on top of OAuth that returns an ID token with identity information.

For web and mobile apps, the recommended flow today is the authorization code flow with PKCE. The older implicit flow is considered obsolete because it passes tokens in the URL. OpenID Connect is the right tool when employees should sign in with their Microsoft account, when you need single sign-on across several applications, or when customers register via Google or Apple. Mobile apps add a few more concerns, which we describe in securing mobile applications.

Build it yourself or use a ready-made solution?

Password hashing with Argon2id or bcrypt, rate limiting against brute force, two-factor authentication, passkeys, password reset, account lockout, audit logs: complete authentication is a lot more than a login form. Ready-made solutions such as Keycloak, Auth.js, Supabase Auth or commercial providers cover all of this and are actively maintained.

For German companies, data location matters too, because user accounts are personal data. A self-hosted Keycloak or a provider with EU data centres makes GDPR compliance easier. This is one of the early architecture decisions that is painful to change later, as our post on SaaS architecture decisions explains.

Frequently asked questions (FAQ)

Is JWT more secure than sessions?

No, neither approach is inherently more secure. Sessions are easier to revoke and usually the sturdier choice for classic web apps. JWTs shine when several services must verify tokens independently.

Where should I store tokens in the browser?

Ideally in an httpOnly cookie with Secure and SameSite set. JavaScript cannot read it there, which limits the damage of an XSS bug. localStorage is not a suitable place for credentials.

Do I need two-factor authentication?

For accounts with access to customer data, payments or administration, you should definitely offer it, and make it mandatory for admins. Passkeys are a convenient alternative that makes phishing much harder and is supported by all major platforms.

What is PKCE and why does it matter?

PKCE is an extension to the OAuth authorization code flow. It prevents an attacker from redeeming an intercepted authorisation code. For public clients such as single-page apps and mobile apps it is now mandatory, and server-side apps should use it too.

Conclusion

Good authentication starts with choosing the simplest method your architecture supports, not the most fashionable one. For many applications that means a session with a properly configured cookie, two-factor login and a proven library instead of home-grown cryptography.

If you are planning an application with user accounts or want to harden your existing login, talk to us about web development.