DevGizmo
Back to Blog
web·

JWT Tokens Explained: Structure, Signing, and Security Best Practices

JSON Web Tokens (JWTs) are widely used for authentication and authorisation. Learn how JWTs are structured, how signing works, the difference between HS256 and RS256, and the critical security rules every developer must follow.

jwtauthenticationsecurityoauth

What Is a JWT?

A JSON Web Token (JWT) is a compact, URL-safe token format defined in RFC 7519. JWTs are commonly used to represent authentication claims between a client and a server.

A JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNjAwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

It is three Base64URL-encoded segments separated by dots: Header.Payload.Signature.

The Three Parts

Header

{
  "alg": "HS256",
  "typ": "JWT"
}

Specifies the signing algorithm and token type.

Payload (Claims)

{
  "sub": "1234567890",
  "name": "Alice",
  "email": "alice@example.com",
  "role": "admin",
  "iat": 1600000000,
  "exp": 1600086400
}

The payload contains claims — statements about the user or other data. Standard claims:

ClaimNameDescription
issIssuerWho issued the token
subSubjectWho the token is about (usually user ID)
audAudienceWho the token is intended for
expExpirationUnix timestamp when the token expires
iatIssued AtUnix timestamp when the token was issued
jtiJWT IDUnique token identifier (for revocation)

Signature

The signature verifies that the token was issued by a trusted party and has not been modified:

HMAC_SHA256(
  base64url(header) + "." + base64url(payload),
  secret_key
)

Signing Algorithms

HS256 (HMAC-SHA256)

Uses a single shared secret key. Both the issuer (server) and the verifier must know the same secret. Suitable for monolithic applications where the same service issues and verifies tokens.

Risk: Any party that can verify JWTs can also issue them. Do not use HS256 in multi-service architectures unless all services are equally trusted.

RS256 (RSA-SHA256)

Uses an RSA key pair: the server signs with the private key, and clients/services verify with the public key. The public key can be distributed openly (e.g., via a JWKS endpoint).

Preferred for:

  • Microservices — each service only has the public key
  • Third-party verification — clients can verify tokens without server round-trips
  • Auth servers (e.g., Auth0, Cognito, Google)

ES256 (ECDSA-SHA256)

Like RS256 but uses shorter elliptic curve keys for equivalent security. Smaller tokens, faster signature generation.

Critical Security Rules

1. Always verify the signature

Never trust a JWT's claims without first verifying the signature. Base64-decoding the payload without verifying gives you untrustworthy data.

2. Validate exp (expiration)

if (payload.exp < Math.floor(Date.now() / 1000)) {
  throw new Error("Token expired");
}

3. Validate aud and iss

An attacker could take a valid JWT from one service and replay it to another. Verify that the token was meant for your service:

if (payload.aud !== "https://api.myservice.com") throw new Error("Invalid audience");
if (payload.iss !== "https://auth.myservice.com") throw new Error("Invalid issuer");

4. Never use alg: "none"

Some early JWT libraries accepted "alg": "none" (no signature) as valid. This completely bypasses security. Libraries must reject alg: none by default.

5. Use short expiration times

Access tokens should expire in minutes (15–60 min), not hours or days. Use refresh tokens (rotated on use) for long-lived sessions.

JWTs Are NOT Encrypted by Default

A standard JWT (JWS — JSON Web Signature) is signed but not encrypted. Anyone who intercepts the token can read the payload by Base64-decoding it. Never include sensitive data (passwords, secrets, PII) in a JWT payload unless using JWE (JSON Web Encryption).

Where to Store JWTs in the Browser

StorageXSS riskCSRF riskNotes
localStorageHighLowAccessible to any JS on the page
sessionStorageHighLowCleared on tab close
HttpOnly cookieNoneModerateCannot be read by JS; CSRF mitigated with SameSite

Best practice: Store JWTs in HttpOnly, Secure, SameSite=Strict cookies. This prevents XSS from stealing the token. Pair with CSRF tokens if using SameSite=None.

Try it yourself

Put these concepts into practice with the free online tool on DevGizmo.