Session & Token Management
A token carries trust: whoever holds it is treated as the user it represents. So how you issue, validate, scope, store, and revoke tokens is one of the most important security decisions in the system. A weak token scheme undoes every careful authorisation check behind it.
Sessions and tokens are where authentication becomes ongoing authorisation. The questions that matter are concrete. Is this token really ours and unchanged? Is it still valid? What exactly does it allow? Where is it stored on the client? And when something goes wrong, can we revoke it fast? Answer those on purpose, and most session attacks have nowhere to land.
The Finperiti sample's symmetric HS256 tokens show the issuance risk: a shared secret means anyone who can verify a token can forge one. But even with strong signing, careless validation, poor storage, or no way to revoke will still sink you.
Issue & validate rigorously
- AlwaysValidate every token fully on every request: signature, issuer, audience, and expiry, on the server, before you trust any claim.
- DoPrefer asymmetric signing (RS256/ES256), so the verification key cannot be used to forge tokens, and key rotation is clean.
- DoKeep tokens short-lived. Use refresh tokens (rotated on use) for longer sessions, rather than long-lived access tokens.
- DoTake identity, roles, and tenant from validated claims only, and re-check authorisation on each request, not just at login.
- ConsiderBinding tokens to context (audience, device, or sender constraints), so a stolen token is harder to replay elsewhere.
- NeverAccept a token without verifying its signature, issuer, audience, and expiry, or honour the
alg: none/ algorithm-confusion family. - NeverTrust an identity, role, or tenant from the request body, query string, or a client-set header instead of the validated token.
var jwt = handler.ReadJwtToken(raw);
var role = jwt.Claims.First(c => c.Type == "role").Value;
This reads claims without validating the signature, issuer, audience, or expiry. An attacker can hand you any claims they like. Always validate before reading.
var result = await handler.ValidateTokenAsync(raw, validationParams);
if (!result.IsValid) return Unauthorized();
var role = result.ClaimsIdentity.FindFirst("role")?.Value;
Claims are only consulted after full validation against the configured signing keys, issuer, audience, and lifetime.
Store, scope & revoke
- DoStore session cookies as HttpOnly and Secure, with SameSite set. Keep browser tokens out of localStorage, where XSS can read them.
- DoScope tokens to the least they need — narrow audience, minimal claims, specific permissions — so a leaked token does limited damage.
- DoProvide a real way to revoke. Invalidate sessions on logout, password change, role change, or suspected compromise, and make it take effect quickly.
- DoRegenerate session identifiers on privilege change (login, elevation) to prevent fixation.
- ConsiderA short access-token lifetime plus refresh-token rotation with reuse detection, so a stolen refresh token is caught and killed.
- Do notPut sensitive data (PII, secrets) inside a token payload. It is signed, not encrypted, and anyone holding it can read it.
- NeverIssue tokens that effectively never expire, or have no way to revoke a session once it is issued.
Self-review checklist
- AskIs this token fully validated (signature, issuer, audience, expiry) before any claim is trusted?
- AskIf this token were stolen, how long would it be useful and how much would it grant?
- AskCan I revoke this session right now if I need to?
- AskWhere does the client store this, and could XSS or a log capture it?