On this page
Choosing authentication is not a contest between old-fashioned sessions and modern tokens. Both can support secure systems, and both can fail through poor storage, missing authorization, or careless expiry handling. The useful question is where trusted state should live and which parties must verify it during an ordinary request.
Separate credential format from transport
A session identifier is usually an opaque reference to server-side state. A JWT is a structured token whose claims can be verified using a signature or MAC. A cookie is a browser transport and storage mechanism. These are different dimensions: you can place an opaque session identifier or a JWT in a cookie.
Use the Unix Timestamp Converter to inspect synthetic expiry values when documenting token behavior. JWT NumericDate values use seconds from the Unix epoch, not JavaScript milliseconds. Never paste a live token into an unrelated debugging tool; encoded claims may contain personal or operational information.
Start with deployment requirements. A single first-party browser application often benefits from simple server-side sessions. An API serving independent services or multiple trusted verifiers may benefit from signed access tokens. Neither choice removes the need for authorization checks on every protected resource.
Understand the server-side session model
After authentication, the server creates a high-entropy session identifier and stores its associated state in a trusted database or session store. The browser presents the identifier on future requests. The server looks it up, checks expiry and account state, then derives the authenticated principal.
The main advantage is central revocation. Deleting or disabling a session can take effect on the next lookup, subject to caching choices. Permission changes are also easier to apply when requests consult current server-side state. The cost is maintaining a reliable shared store and deciding how session reads behave during outages.
Rotate the session identifier after login and privilege changes to prevent fixation. Set both absolute and idle expiry according to risk. Logging out should invalidate the server record as well as clear the browser cookie; deleting the cookie alone leaves a stolen identifier usable until its server-side expiry.
The OWASP Session Management Cheat Sheet covers these lifecycle details. Treat them as part of the design, not as optional cleanup after the login screen works.
Understand what JWT validation actually requires
A signed JWT lets a verifier check claims without looking up a session record on every request. This can simplify communication among services, but the token's contents generally remain readable. Signing is not encryption, and a valid signature does not prove the token was meant for your particular endpoint.
import { jwtVerify } from 'jose';
const { payload } = await jwtVerify(token, trustedPublicKey, {
issuer: 'https://identity.example.com',
audience: 'orders-api',
algorithms: ['RS256']
});
if (typeof payload.sub !== 'string' || !payload.sub) {
throw new Error('Missing subject');
}This example assumes trusted key loading and a token format issued by your identity system. Configure verification to require the expiry and other claims your policy mandates; accepting a token without an expected claim can weaken the intended contract. Do not choose algorithms based on untrusted input alone.
Follow JWT best current practices, including algorithm verification and audience separation. Distinguish access tokens from identity tokens, refresh tokens, and other signed objects. Tokens issued for one purpose should not become acceptable everywhere merely because the same issuer signed them.
Compare revocation, storage, and browser threats
JWTs are often called stateless, but real systems still need account records, key rotation, refresh-token state, and sometimes revocation lists. A self-contained access token can remain valid after logout unless the API checks additional state or the token expires. Short access-token lifetimes reduce that exposure but do not eliminate it.
For browser sessions, use an appropriately scoped cookie with Secure, HttpOnly, and a deliberate SameSite policy. A host-only __Host- cookie also requires Path=/ and no Domain attribute. HttpOnly prevents JavaScript from reading the cookie, but does not prevent malicious script from making authenticated requests from the page.
Cookies are sent automatically under browser rules, so assess CSRF protections for state-changing requests. A bearer token explicitly added to an Authorization header changes the CSRF exposure, but storing it in browser-accessible storage can increase token-theft risk from script execution. Neither transport creates universal protection.
Read MDN's cookie guide for attribute behavior. Account for cross-site login flows and embedded applications rather than applying the strictest SameSite setting without testing the actual navigation pattern.
Choose a lifecycle your team can operate
For a first-party application, begin with a framework's mature session implementation unless requirements clearly justify more complexity. Decide how sessions are enumerated, revoked, rotated, and audited. Test password changes, account suspension, and concurrent sessions across devices.
For distributed access tokens, define key rotation and cache behavior. Verifiers need trusted issuer metadata and a bounded strategy for refreshing keys. Do not fetch keys from arbitrary URLs supplied by a token. Consider what happens when an unfamiliar key identifier appears during an identity-provider outage.
If refresh tokens are issued, protect them separately and implement the provider's rotation and reuse-detection rules. A refresh flow can be stateful even when ordinary access-token verification is local. Document how quickly revocation must propagate and which components enforce it.
Use the JSON Formatter only on synthetic claims to review your data-minimization policy. Avoid embedding frequently changing permissions, private profile data, or large account objects without a concrete reason. Large tokens add request overhead and make data exposure harder to control.
Finally, record the decision in your backend architecture reference. Include expected outage behavior, logout semantics, browser storage, expiry rules, and how protected resources check ownership. Authentication establishes a principal; a valid session or token must never become blanket permission to read another customer's records.
Key takeaways
- Compare session state and signed-token verification separately from cookie transport.
- Prefer a lifecycle that supports your real revocation and operating requirements.
- Validate issuer, audience, algorithms, expiry policy, and token purpose deliberately.
- Address browser threats and resource authorization regardless of credential format.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor