Decoding JWTs Without Verification
Understand when and why JWTs are decoded without signature verification, the security risks involved, legitimate use cases, and why you must never trust unverified tokens.
Detailed Explanation
Decoding a JWT means reading the header and payload by base64url-decoding them, without checking whether the signature is valid. This is a common operation in debugging, client-side applications, and routing logic, but it comes with critical security considerations that every developer must understand.
How decoding works:
// Decoding (NOT verifying) a JWT
const [headerB64, payloadB64] = token.split('.');
const header = JSON.parse(atob(headerB64));
const payload = JSON.parse(atob(payloadB64));
// WARNING: These values are NOT verified!
Because the header and payload are merely base64url-encoded (not encrypted), anyone can read them. Decoding requires no keys, secrets, or cryptographic operations. This is by design: JWTs provide integrity (via the signature) and optionally confidentiality (via JWE encryption), but the standard JWT format prioritizes compact representation over payload secrecy.
Legitimate use cases for decoding without verification:
Client-side display: A frontend application might decode the access token to display the user's name or check the expiration time for proactive token refresh. The client is not making authorization decisions based on these values, just improving the user experience. Routing decisions: An API gateway might read the iss claim to determine which key to use for verification, or read the aud claim to route the request to the correct backend service. Debugging: Developers paste tokens into decoder tools (like this one) to inspect claims during development and troubleshooting.
The critical security rule:
Never make authorization or authentication decisions based on unverified token claims. An attacker can craft a JWT with any claims they want (e.g., "role": "admin") and send it to your server. If your server decodes the token and uses the claims without verifying the signature, the attacker has full control. This is equivalent to trusting user input without validation.
Server-side verification is mandatory:
Every server that acts on JWT claims must verify the signature before trusting the payload. The decode-only approach is appropriate only for non-security-critical operations: displaying information, logging, routing to the correct verification path, or client-side UX improvements. The moment a claim influences an access control decision, verification is required.
Common mistake in JavaScript:
Some JWT libraries offer both decode() and verify() functions. Using decode() on the server for authentication is a critical vulnerability. Always use verify(), which decodes and validates the signature in one step.
Use Case
A React frontend decodes the JWT access token to read the user's name and token expiration for UI display, while the backend API always performs full signature verification.