← Back to Security

Cookies vs Sessions vs JWT vs PASETO

Cookies, sessions, JWT, and PASETO compared by state, transport, and revocation.

SecurityAuthenticationSecurity

These four terms are often compared as if they were interchangeable authentication systems, but they live at different layers. A cookie is a browser storage and transport mechanism. A session is server-side state associated with a client. JWT and PASETO are token formats that can carry claims. The right choice depends less on fashion and more on where you want state to live, how revocation should work, and what attack surface you are willing to manage.

Cookies are simply name-value pairs that a browser stores and sends back on matching requests. By themselves they do not prove identity. They become part of authentication when the cookie contains a session identifier or some other credential. Their strength is that the browser can enforce useful controls such as HttpOnly, Secure, SameSite, domain scoping, and expiry. Those flags matter because many web auth bugs are really token handling bugs.

A session-based design stores authoritative state on the server. After login, the server issues a session ID, often via cookie, and keeps the real user state in memory or a shared session store. The main advantage is control. You can revoke a session immediately, rotate privileges centrally, and keep sensitive claims off the client. The tradeoff is operational: every request may require a lookup, and horizontally scaled systems need a shared session backend or sticky routing.

JWTs move more state to the client by embedding claims inside a signed token. That makes verification cheap for distributed services because a service can validate the signature locally without calling a central session store. This is attractive for APIs and service meshes, but it changes revocation semantics. Once a token is issued, it often remains valid until expiry unless you add extra machinery such as deny lists, short lifetimes, refresh tokens, or token introspection.

PASETO aims to reduce a class of foot-guns seen in JWT implementations by standardising safer cryptographic choices and removing algorithm negotiation from application code. In practice, that means fewer chances for developers to accept an unsafe algorithm or misconfigure signature verification. It does not remove the need for sound token lifecycle design. A badly scoped long-lived PASETO is still a bad credential.

The most common mistake is to choose token format before choosing trust boundaries. For a browser-based application that needs fast revocation and simple security posture, an HttpOnly secure cookie that references a server-side session is often the most boring and effective choice. For machine-to-machine APIs where stateless verification matters, signed tokens may be a better fit. In both cases, refresh strategy, key rotation, and replay protection still need design work.

You should also separate authentication from authorisation. A token may say who the user is and when they authenticated, but fine-grained permissions often change more often than tokens do. Embedding every role and entitlement into a long-lived token can create stale authorisation bugs.

The comparison is therefore not cookie versus JWT in some abstract sense. It is server-managed state versus self-contained credentials, plus a question of transport. Choose the mechanism that matches revocation needs, client type, scaling model, and threat model, then harden the boring details such as expiry, storage, rotation, and auditability.