JWT Authentication Implementation Guide: Issuing, Verification and Refresh Tokens

JSON Web Token (JWT) is an open standard (RFC 7519) for securely transmitting information between parties in a compact, self-contained way. Because tokens are digitally signed, receivers can verify their integrity and origin. jwt.io is the industry-recognized reference, and this guide follows its official documentation along with Auth0's engineering practices to outline a workable JWT authentication scheme. JWT is often paired with our OAuth2 and JWT API Security Guide; this article focuses on the token itself.

JWT Structure: Three Parts

A JWT consists of three dot-separated parts: xxxxx.yyyyy.zzzzz.

  1. Header: declares the token type and signing algorithm, e.g. {"alg":"HS256","typ":"JWT"}.
  2. Payload: contains the claims — registered claims such as iss (issuer), exp (expiration), sub (subject), and aud (audience), plus custom claims. Note that signed token content is readable by anyone, so never put secrets such as passwords or keys in the payload.
  3. Signature: computed by signing base64UrlEncode(header) + "." + base64UrlEncode(payload) with a key, proving the content was not tampered with and was signed by the party holding the private key or secret.

Pasting a real token into jwt.io's Debugger makes it click (demonstration HS256 token below):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

The middle payload is just Base64 — anyone can decode it and read the name field, which is a perfect illustration of why you never put sensitive data in the payload.

Issuing and Verifying

Issuing flow: after a successful login, the server generates a JWT and returns it to the client. Verification flow: the client sends the token in the Authorization header on subsequent requests:

Authorization: Bearer <token>

The server should distinguish two layers: validation checks whether the token is well-formed and its claims are enforceable (not expired, not used before its time); verification re-signs the header and payload with the algorithm and key to confirm the token is genuine and unmodified, and checks that iss and aud match expectations. Mature official libraries in every language wrap these steps — do not hand-roll signature logic.

With Node.js's jsonwebtoken, issuing and verifying each take a few lines:

const jwt = require('jsonwebtoken');

// Issue after a successful login; keep expiry short
const token = jwt.sign({ userId: user.id, role: 'admin' }, SECRET, {
  expiresIn: '15m',
  issuer: 'my-api',
});

// Verify on each request
 try {
  const payload = jwt.verify(token, SECRET, {
    issuer: 'my-api',
    algorithms: ['HS256'],
  });
  req.user = payload;
} catch (err) {
  res.status(401).json({ error: 'invalid token' });
}

Passing an explicit issuer and an algorithms allowlist at verification blocks "algorithm confusion" attacks (changing alg to none or downgrading to a weak algorithm is rejected outright).

Refresh Tokens: Extending Sessions

Access tokens usually expire quickly (15 minutes to an hour). Rather than forcing a re-login, a refresh token is used to obtain a new access token. Auth0's recommended practices:

  • Refresh tokens live much longer (days to months) and should only be issued to trusted clients.
  • Refresh tokens must be revocable: the server keeps their identifiers and invalidates them immediately on suspected compromise or logout.
  • The refresh endpoint must verify the refresh token's own signature and expiry, returning a fresh access token and optionally a rotated refresh token.

The division of labor between the two tokens:

Token Lifetime Where it lives Purpose
access token 15 min-1 hour Memory or short-lived variable Sent with every API request
refresh token Days to months Trusted clients only, HttpOnly cookie Exchanges for a new access token

Putting the refresh token in an HttpOnly cookie keeps JavaScript from reading it, sharply narrowing the XSS surface; keeping the access token in memory rather than localStorage means a page refresh simply re-runs the refresh flow.

A refresh token is a long-lived credential — protect it like a password, and avoid storing it in browser localStorage, which is vulnerable to XSS exfiltration.

Security Best Practices

  • Short expiry plus refresh: keep access tokens short to reduce the exposure window, and renew with refresh tokens.
  • Never put sensitive data in the payload: it is Base64-readable; no keys or internal IDs.
  • Mind header size limits: tokens travel in HTTP headers, some servers cap at 8KB, so avoid bloating them with permission claims.
  • Choose algorithms deliberately: HS256 (shared secret) suits internal systems; RS256/ES256 (public/private key pairs) let other services verify the issuer.
  • Use mature libraries: jwt.io's Libraries page lists recommended implementations per language, avoiding the pitfalls of hand-written crypto.

Another easy-to-miss detail is key rotation: with HS256 the shared secret is the root of trust, and once leaked, every token ever issued becomes untrustworthy. Rotate periodically, and during the transition let the verifier accept both the old and new keys so you do not kick every online user out at once. For auditing, log issuance, verification failures, and refresh counts — a sudden spike in any of them usually signals scanning or brute-force attempts.

For session-based website login, read API Security OAuth2 and JWT Guide first; to integrate middleware, follow Node.js Express Backend Development Guide or Python FastAPI Backend Development Guide.

16IDC perspective

For indie developers and small teams, JWT is the highest value API authentication scheme: stateless, cross-language, and easy to scale horizontally. But stateless also means a token cannot be actively revoked once issued, so revocable refresh tokens and sane expiry times matter greatly. Plan your auth middleware, rate limiting, and API Error Handling and Retry Strategy together before launch. More backend engineering practices live in the Backend Integration category.

Reference: jwt.io introduction https://jwt.io/introduction · RFC 7519 (JSON Web Token) https://datatracker.ietf.org/doc/html/rfc7519 · Auth0 refresh token practices https://auth0.com/docs/secure/tokens/refresh-tokens
Source: https://jwt.io/introduction