How to Decode and Verify JWT Tokens (2026 QA Guide)
Decode, verify, and debug JWT tokens the safe way. Covers HS256, RS256, ES256, JWKS lookup, expiry checks, and how to test JWT auth in Postman, Playwright, and Rest Assured.

2026-07-17 · By Avinash K
JWTs (JSON Web Tokens) power almost every modern API auth flow — OAuth 2.0, OpenID Connect, service-to-service auth. As a QA engineer you will decode, tamper with, expire, and forge them daily. This guide shows the right way to do all four without leaking secrets or trusting the wrong signature.
Anatomy of a JWT
A JWT is three base64url-encoded parts joined by dots: header.payload.signature. The header declares the algorithm (HS256, RS256, ES256…), the payload carries claims (sub, exp, iat, aud, custom fields), and the signature is what proves the token was issued by someone holding the key.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJleHAiOjE3MzUwMDAwMDB9.abc123...Decoding is not verifying — the #1 mistake
Any JWT can be decoded because base64url is not encryption. Anyone with the token can read the payload. Verifying is separate: it checks the signature against the issuer's key and confirms the claims (exp, nbf, iss, aud). A QA suite that only decodes is testing nothing about auth.
Verify HS256 in three lines
// Node / Playwright
import jwt from "jsonwebtoken";
const claims = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ["HS256"] });
expect(claims.sub).toBe(userId);Always pin algorithms. If you omit it, an attacker can send alg: none or downgrade RS256 to HS256 and pass your public key as the HMAC secret.
Verify RS256 / ES256 via JWKS
For OAuth providers (Auth0, Okta, Cognito, Google) fetch the JWKS from /.well-known/jwks.json, pick the key whose kid matches the token header, then verify. Cache the JWKS for 10 minutes to avoid rate limits.
import { createRemoteJWKSet, jwtVerify } from "jose";
const jwks = createRemoteJWKSet(new URL(iss + "/.well-known/jwks.json"));
const { payload } = await jwtVerify(token, jwks, { issuer: iss, audience: aud });Common expiry & clock-skew bugs
- exp in seconds, not ms.
Date.now()/1000— a common off-by-1000 leaves tokens "always expired". - Clock skew. Allow ±30s (
clockTolerance: 30) or CI flakes when the runner drifts. - nbf not-before. Tokens rejected for a few seconds after issuance if
nbf > now.
Decode safely in one click
Pasting production tokens into jwt.io sends them over the wire. The free JWT Debugger runs 100% in your browser — no network calls — and adds a 15-check vulnerability audit (alg=none, weak HS secrets, jku/kid injection, sensitive-claim leakage, missing exp), live expiry countdown, and one-click export to Postman, Playwright, Rest Assured, cURL, Axios, and Fetch.