Back to Blog

How to Decode JWT Tokens — A Developer's Complete Guide

JSON Web Tokens (JWT) are an open, industry standard (RFC 7519) method for representing claims securely between two parties. They are widely used for authentication and authorization in modern web applications.

What is a JWT?

A JWT consists of three parts separated by dots (.):

header.payload.signature
  1. Header: Contains the token type (usually "JWT") and the signing algorithm being used (e.g., HMAC SHA256 or RSA).
  2. Payload: Contains the claims (the statements about an entity, typically the user, and additional data).
  3. Signature: Used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn't changed along the way.

Both the header and payload are Base64URL encoded JSON objects. Because they are only encoded, not encrypted, anyone can decode and read them. Never put sensitive information (like passwords) inside a JWT payload.

Base64URL vs Base64

Base64URL is a variant of Base64 designed specifically for use in URLs and filenames. It replaces + with - and /= with _, and omits the trailing = padding characters. When decoding a JWT manually, you may need to add the padding back and replace the characters to use standard Base64 decoders.

Standard Claims

The JWT specification defines several standard claims (registered claims):

  • iss (Issuer): Identifies the principal that issued the JWT.
  • sub (Subject): Identifies the principal that is the subject of the JWT.
  • aud (Audience): Identifies the recipients that the JWT is intended for.
  • exp (Expiration Time): Identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. (Unix timestamp)
  • nbf (Not Before): Identifies the time before which the JWT MUST NOT be accepted for processing.
  • iat (Issued At): Identifies the time at which the JWT was issued.

Decoding JWT without a Library

You can decode the header and payload of a JWT natively in most languages.

JavaScript (Browser)

In the browser, you can use the built-in atob() function (though it doesn't support full UTF-8 decoding properly without extra steps, it works for simple ASCII payloads):

function decodeJwtPayload(token) {
  const base64Url = token.split('.')[1];
  const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
  const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
      return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
  }).join(''));
  
  return JSON.parse(jsonPayload);
}

Python

Using the built-in base64 and json modules:

import base64
import json

def decode_jwt_payload(token):
    payload_part = token.split('.')[1]
    # Add padding back if necessary
    payload_part += '=' * (-len(payload_part) % 4)
    # Decode Base64URL to bytes, then to string
    decoded_bytes = base64.urlsafe_b64decode(payload_part)
    return json.loads(decoded_bytes.decode('utf-8'))

Common Security Mistakes

  1. Assuming JWTs are encrypted: They are encoded. The payload is readable by anyone who has the token.
  2. Accepting "none" algorithm: Always enforce a strong signing algorithm (like RS256 or HS256). The "none" algorithm vulnerability allowed attackers to forge tokens.
  3. Not verifying the signature: Decoding the payload is not enough; you must cryptographically verify the signature before trusting the claims.
  4. Ignoring expiration (exp): Always check that the token is not expired before trusting it.

Need to decode a token right now securely in your browser? Use our JWT Decoder tool.