This guide is written by the team that builds Casdoor and describes the standards as published (listed under Sources), with notes on how Casdoor implements them as of October 21, 2026. Spotted a mistake? Tell us.
After a user signs in, every later request has to prove it comes from that user. There are two common ways to carry that proof:
- A session: the server stores the login state and gives the browser a random session ID in a cookie. Each request sends the cookie, and the server looks the session up.
- A JWT (JSON Web Token): the server signs a token that contains the user's identity and an expiry time. Each request sends the token, and the server checks the signature without looking anything up.
Short version: use server-side sessions for your own web app's browser login, and short-lived JWT access tokens for APIs and services. That's what most identity providers do internally.
At a glance
| Server-side session | JWT | |
|---|---|---|
| Where state lives | On the server (database, Redis) | In the token itself |
| What the client holds | A random ID, meaningless on its own | A signed, readable token with claims |
| Checking a request | Look up the session | Verify the signature and expiry |
| Revoking access now | Delete the session | Hard: the token is valid until it expires, unless you add a check |
| Size | A short cookie | Often 500 bytes to a few KB |
| Works across services and domains | Each service needs the session store | Any service with the public key can verify it |
| Typical use | Browser login to a web app | API access tokens, service-to-service calls, OIDC ID tokens |
The case for sessions
Sessions are simple and easy to secure. The cookie holds no information, so leaking it reveals nothing about the user, and with HttpOnly, Secure and SameSite set, JavaScript can't read it and other sites can't send it. Signing a user out, or signing them out everywhere after a password change, is a delete in the session store. The cost is a lookup per request, which a database index or Redis makes cheap.
The case for JWTs
A JWT carries its own proof, so an API, a microservice or a server in another region can check it with the issuer's public key and no shared store. That's why OAuth 2.0 access tokens and OpenID Connect ID tokens are usually JWTs, and why they work well for mobile apps, single-page apps calling APIs on another domain, and calls between services.
The cost is revocation. A stolen or no-longer-wanted JWT stays valid until it expires. The usual answers are:
- Short lifetimes (minutes to an hour) with refresh tokens that the server can revoke.
- Introspection (RFC 7662) for sensitive APIs: ask the issuer whether the token is still active.
- A deny list of revoked token IDs (
jti) for the remaining lifetime.
Security pitfalls
- Storing JWTs in
localStorage. Any script on the page, including injected ones, can read it. For browser apps, prefer anHttpOnlycookie or keep tokens in memory behind a backend. - Accepting unsigned or wrongly signed tokens. Pin the expected algorithm, reject
none, and validateiss,audandexp(RFC 8725). - Putting secrets in the payload. A JWT is signed, not encrypted: anyone holding it can read the claims.
- Long-lived JWTs without revocation, which turn a stolen token into a long-term key.
- For sessions: regenerate the session ID after login to prevent fixation, and expire idle sessions.
Sessions and JWTs together
Identity providers combine the two. Casdoor, for example, keeps a server-side session for each browser sign-in, recording the device, browser, IP address and last activity, and issues JWT access and ID tokens to applications. Each token is stored with the session it was issued in, so when a user signs out of a session, an admin ends it on the Sessions page, or the user's password changes, Casdoor revokes the matching tokens: the introspection endpoint then answers "active": false and the refresh token stops working. Applications can choose the JWT format, from the full user object to standard OIDC claims only.
Self-host Casdoor for free, or run it on Casdoor Cloud from $24/month billed yearly. See also OAuth 2.0 vs OpenID Connect and single logout.
FAQ
Are JWTs more secure than sessions? No. Both are secure when implemented carefully. Sessions are easier to get right and to revoke; JWTs are easier to verify across many services.
Can I use a JWT inside a cookie?
Yes, and for browser apps it's often the best place: an HttpOnly, Secure cookie protects the token from scripts. You still have the JWT's revocation trade-off.
Do JWTs scale better? They avoid a lookup per request, which matters across many services or regions. For a single web app, a session lookup in Redis or an indexed table is rarely the bottleneck.
What should a mobile app use? The OAuth 2.0 authorization code flow with PKCE, a short-lived access token for API calls, and a refresh token kept in the platform's secure storage.
Sources
- RFC 7519: JSON Web Token
- RFC 8725: JWT Best Current Practices
- RFC 7662: OAuth 2.0 Token Introspection
- OWASP Session Management Cheat Sheet
- Casdoor docs: Tokens
- Casdoor docs: Sessions
Product names are trademarks of their respective owners.