Tokens: Access, Refresh, and JWTs

Tokens are the currency of modern identity — they carry proof of authorization and identity across every request. But "token" hides real distinctions: access versus refresh versus ID tokens do different jobs, and a JWT you can read but must validate correctly is a razor that cuts both ways. Most identity vulnerabilities live in how tokens are issued, stored, and checked.

The flows post ended with the client holding tokens. This post asks: what is a token, what kinds are there, and — critically — how do you validate one without introducing a vulnerability? Tokens are where authorization and authentication become concrete artifacts that travel with requests, and mishandling them (bad validation, insecure storage) is behind a large share of identity breaches. Getting tokens right is getting identity right.

What a token is

A token is a string that represents a granted permission or a verified identity — a credential the client presents to prove it’s allowed to do something, so it doesn’t have to re-authenticate on every request. Broadly, tokens come in two structural styles:

Both are legitimate; they trade off differently (opaque = easy to revoke, needs a lookup; JWT = self-contained and fast, harder to revoke — more below). The style matters for how you validate and revoke, which is where security lives.

The three tokens and their jobs

Modern identity (OAuth + OIDC) issues up to three token types, and confusing their purposes is a classic, dangerous error:

  access token  → sent to Resource Server (API)   → "what can I access?"  (authorization)
  refresh token → sent to Authorization Server     → "give me a new access token"
  id token      → consumed by the Client           → "who is the user?"    (authentication)

The cardinal rule: use each token for its job. Sending an ID token to an API, or treating an access token as identity, or exposing a refresh token to the browser — each is a security mistake. The token type encodes its purpose; respect it.

Anatomy of a JWT

Since JWTs (JSON Web Tokens) dominate modern identity, understanding their structure is essential. A JWT is three Base64URL-encoded parts separated by dots:

   header  .  payload  .  signature
   {alg,typ}  {claims}    crypto signature over header+payload

header:    algorithm and token type
payload:   claims — e.g. sub (subject/user id), iss (issuer),
           aud (audience), exp (expiry), iat (issued-at), scopes, roles
signature: signs the header+payload with the issuer's key

Two crucial properties:

Validating a JWT correctly

This is the security heart of the post, because incorrect JWT validation is a common, severe vulnerability. A JWT is only trustworthy if you validate it properly — accepting one you haven’t fully checked is accepting forged identity. Correct validation means checking all of:

Skipping any of these is a hole. The safe path is to use a well-maintained, vetted library for JWT validation rather than hand-rolling it — the edge cases (algorithm confusion, key handling, clock skew) are exactly where hand-written validation fails. Validate signature + expiry + issuer + audience, always, with a trusted library.

The revocation trade-off, and token storage

JWTs’ self-contained nature creates their main weakness: they’re hard to revoke. Because a valid signature and unexpired exp are sufficient, a JWT is accepted until it expires even if you want to revoke it early (the user logged out, the token leaked) — there’s no server-side state to invalidate. The standard mitigations:

And token storage is its own security-critical decision, especially in browsers:

Tokens are where identity becomes tangible and where much can go wrong: use each token for its purpose, never put secrets in a JWT, validate signature/expiry/issuer/audience with a trusted library, keep access tokens short-lived, and guard refresh tokens above all. The next post covers the token that answers “who are you” — the ID token — and the protocol that adds it: OpenID Connect.

Key takeaways

Further reading

Sources & References

The JWT specification