Contents

For a web app whose front end and backend share a domain, sessions in a secure cookie are the most reliable choice: simple, easy to revoke and protected by the browser from script theft. JWTs suit mobile apps and service-to-service calls but need careful revocation and storage. OAuth and OpenID Connect are not alternatives but standards for external sign-in and third-party access.

Here is how the approaches differ, when to choose each and which mistakes are most common.

How does authentication differ from authorisation?

Authentication answers "who is this"; authorisation answers "what may they do". Sessions, JWTs and OAuth mostly solve the first problem and carry that information between requests; checking permissions on specific data and actions is a separate layer needed whatever sign-in method you use.

Authorisation failures — a user reaching someone else's data — top the risk list in the OWASP Top 10. So choosing a sign-in mechanism does not remove the need to check permissions on every request.

How do sessions, JWTs and OAuth work?

A session keeps sign-in state on the server, and the browser sends only its ID in a cookie. A JWT is a signed token that contains the user data itself, so the server can verify it without a storage lookup. OAuth and OpenID Connect describe how to obtain such tokens from an identity provider — Google, a corporate directory or your own authorisation server.

Criterion Cookie sessions JWT OAuth / OpenID Connect
Where state lives On the server Inside the token With the identity provider
Revoking access Instant: delete the session Harder: the token lives until expiry Via the provider and short-lived tokens
XSS protection HttpOnly cookies are hidden from scripts Depends on where the token is stored Depends on the client implementation
Suits Web on one domain Mobile apps, service-to-service External sign-in, third-party access
Complexity Low Medium High; prefer ready-made solutions

A smartphone with a lock icon on a desk Photo: Unsplash

When should you choose sessions?

Choose sessions for a web app whose front end and API sit on one domain or its subdomains. It is the simplest and safest option: a cookie with HttpOnly, Secure and SameSite flags is invisible to scripts, and signing out or blocking a user takes effect instantly.

// config/session.php — secure session cookie settings
'secure'    => true,   // HTTPS only
'http_only' => true,   // hidden from JavaScript
'same_site' => 'lax',  // protects against cross-site requests
'lifetime'  => 120,    // idle minutes before sign-out

A Vue or React SPA on the same domain can also use sessions — many frameworks ship ready packages for this, such as Sanctum in Laravel.

When do you need JWTs and OAuth?

You need JWTs when the client cannot reliably use cookies or a request passes through several services: mobile apps, partner APIs, microservice communication. You need OAuth and OpenID Connect when users sign in with an external account or a third-party app accesses data on a user's behalf.

Rules for working with JWTs

  1. Short-lived access tokens — minutes, not days.
  2. Refresh tokens stored securely and revocable on the server.
  3. On mobile, tokens go in the system's secure storage; in the browser, never in localStorage.
  4. Always verify the signature, with the algorithm fixed on the server rather than read from the token.
  5. No secrets in the token — anyone can read its contents.

Building your own OAuth server is almost never worth it: there are ready identity providers and proven libraries for that.

Which mistakes are most common?

The most common mistakes involve token storage, token lifetime and permission checks: the token sits in localStorage and is stolen via XSS, lives for a month with no revocation, and the server checks that the user is signed in but not that the object belongs to them.

Typical mistakes:

  • JWTs with no revocation — a departed employee keeps access until the token expires;
  • passwords without slow hashing or without login attempt limits;
  • no two-factor authentication for administrators;
  • different sign-in mechanisms for web and API without unified permission checks;
  • sign-in error messages that reveal whether a user exists.

We covered designing APIs so permissions are checked consistently in how to design a REST API.

In short

  • Authentication is who the user is, authorisation is what they may do; check permissions on every request.
  • For web on one domain, sessions in an HttpOnly cookie are the best choice.
  • JWTs suit mobile apps and service-to-service calls, with short lifetimes and revocation.
  • OAuth and OpenID Connect are for external sign-in; use ready-made solutions.
  • Never store tokens in localStorage, and enable two-factor authentication for administrators.

FAQ

  • Can a JWT be stored in localStorage?

    Better not: any XSS vulnerability lets an attacker's script read the token. In the browser an HttpOnly cookie is safer; in a mobile app, the system's secure storage.

  • Is two-factor authentication necessary?

    Mandatory for administrators and staff with access to customer data. Offer it to regular users, and in financial products it is often required.

  • What are passkeys?

    Passwordless sign-in based on cryptographic keys stored on the user's device and unlocked with biometrics or a PIN. Passkeys resist phishing and are gradually becoming the standard way to sign in.