How to Decode JWT Tokens in Your Browser (OAuth2 & OIDC Guide)
To decode a JSON Web Token (JWT) in your browser, split its three period-delimited Base64URL strings—header, payload, and signature—and decode the Base64URL data into JSON objects. You can decode JWTs securely in real time using an in-browser JWT Extractor without uploading secret keys or bearer tokens to external servers.
Key definitions: RFC 7519, Base64URL, and JWT terminology
Modern web security frameworks like OAuth 2.0 and OpenID Connect (OIDC) rely on standardized cryptographic specifications to transfer user identity and access permissions between services:
- JWT (JSON Web Token – RFC 7519): An open standard defining a compact, URL-safe container for transferring JSON-encoded identity claims securely between two parties.
- Base64URL Encoding (RFC 4648 §5): A modified variant of Base64 encoding designed specifically for transmission in URLs and HTTP headers. It replaces
+with-,/with_, and removes trailing=padding characters. - Header: The first section of a JWT containing cryptographic metadata, such as the signature algorithm (
alg) and token type (typ). - Payload (Claims Set): The middle section containing contextual claims—statements about an entity (such as user IDs, roles, scope permissions, and expiration timestamps).
- Cryptographic Signature: The third section produced by signing the Base64URL-encoded header and payload with a secret key or asymmetric key pair to guarantee data integrity.
- Claims: Key-value pairs inside the JWT payload body representing identity assertions or token validity conditions.
- Bearer Token (RFC 6750): A security token where any client possessing the token string can access protected API endpoints without proving key possession.
Anatomy of a JSON Web Token (JWT)
A JSON Web Token consists of three distinct parts separated by period (.) characters. The structural format follows this layout:
[header].[payload].[signature]
A typical encoded JWT string appears as follows:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImF1dGgtMTIzIn0.eyJzdWIiOiJ1c2VyXzk4NzY1NCIsImlzcyI6Imh0dHBzOi8vYXV0aC5leGFtcGxlLmNvbSIsImF1ZCI6ImFwaS5leGFtcGxlLmNvbSIsImV4cCI6MTc5MDI4ODAwMCwiaWF0IjoxNzkwMjUyMDAwLCJyb2xlcyI6WyJhZG1pbiIsImVkaXRvciJdfQ.d3m0X3hhbXBsZV9zaWduYXR1cmVfZGF0YV9mb3JfZGVtb25zdHJhdGlvbl9vbmx5...
1. The Header: Cryptographic algorithm and metadata
The header specifies the cryptographic primitives and token metadata used to generate and verify the token. Once decoded from Base64URL, the header yields a standard JSON object:
{
"alg": "RS256",
"typ": "JWT",
"kid": "auth-key-2026-v1"
}
alg(Algorithm): Specifies the signing algorithm. Common algorithms includeHS256(HMAC using SHA-256),RS256(RSASSA-PKCS1-v1_5 using SHA-256), andES256(ECDSA using P-256 and SHA-256).typ(Type): Specifies the token type, which is typically set toJWT.kid(Key ID): A hint indicating which public key in a JSON Web Key Set (JWKS) was used to sign the token.
2. The Claims Payload: Identity assertions and permissions
The payload contains the actual assertions (claims) transferred between the authorization server and the resource server. Decoded payload JSON contains identity data and authorization attributes:
{
"sub": "user_987654",
"iss": "https://auth.example.com/",
"aud": "https://api.example.com/",
"exp": 1790288000,
"nbf": 1790252000,
"iat": 1790252000,
"jti": "b3e91f6a-4d2c-482a-9e12-881a4ef71201",
"email": "alex.developer@example.com",
"roles": ["admin", "editor"]
}
3. The Cryptographic Signature: Verification and tamper prevention
The signature is calculated by taking the Base64URL-encoded header, appending a period, appending the Base64URL-encoded payload, and signing that string with a secret key or private key:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secretKey
)
The signature ensures that the payload cannot be altered in transit. If an attacker modifies a claim (such as changing a user role from user to admin), the signature will no longer match during verification.
Standard registered JWT claims breakdown
IETF RFC 7519 defines seven standard registered claims. While optional, these claims provide interoperable identity and session rules across OAuth 2.0 implementations:
| Claim Key | Full Name | Datatype | RFC 7519 Specification & Purpose | Example Value |
|---|---|---|---|---|
sub |
Subject | String | Identifies the principal subject of the JWT (e.g., User ID or Account ID). Must be unique within the issuer scope. | "user_987654" |
iss |
Issuer | String / URI | Identifies the principal that issued the token. Typically matches the authorization server domain. | "https://auth.example.com/" |
aud |
Audience | String / Array | Identifies the recipients that the JWT is intended for. API gateways reject tokens where audience does not match their identifier. | "https://api.example.com/" |
exp |
Expiration Time | NumericDate | Identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. Defined as seconds since Unix epoch. | 1790288000 |
nbf |
Not Before | NumericDate | Identifies the time before which the JWT MUST NOT be accepted for processing. | 1790252000 |
iat |
Issued At | NumericDate | Identifies the time at which the JWT was issued. Used to determine token age. | 1790252000 |
jti |
JWT ID | String | Provides a unique identifier for the JWT. Used to prevent token replay attacks. | "b3e91f6a-4d2c..." |
Converting Unix timestamps to human-readable dates
JWT timestamps like exp, iat, and nbf store time as integer seconds elapsed since 1970-01-01T00:00:00Z UTC (NumericDate format). To convert these integers into human-readable timestamps across different development environments, use the following code patterns:
// JavaScript (Browser & Node.js)
const expTimestamp = 1790288000;
const date = new Date(expTimestamp * 1000);
console.log(date.toISOString()); // Outputs: "2026-09-24T00:00:00.000Z"
console.log(date.toLocaleString()); // Localized format
// Check token expiry with 60 seconds clock skew tolerance
const isExpired = Date.now() >= (expTimestamp * 1000);
console.log("Token Expired:", isExpired);
# Python
import datetime
exp_timestamp = 1790288000
exp_date = datetime.datetime.fromtimestamp(exp_timestamp, tz=datetime.timezone.utc)
print(exp_date.isoformat()) # Outputs: 2026-09-24T00:00:00+00:00
# Bash / Linux Terminal
date -u -d @1790288000
# Output: Thu Sep 24 00:00:00 UTC 2026
How Base64URL decoding works in JavaScript
Standard Base64 encoding uses + and / characters and appends = padding at the end. Because +, /, and = have special meanings in URL parameters and HTTP headers, RFC 7519 mandates Base64URL encoding for JWTs.
Base64 vs Base64URL conversion rules
- Replace hyphens (
-) with plus signs (+). - Replace underscores (
_) with forward slashes (/). - Calculate string length modulo 4 (
length % 4) and append missing=padding characters (1 or 2 equal signs).
Robust client-side JavaScript Base64URL decoder
Standard atob() in web browsers throws errors if the Base64 string contains URL-safe characters or multi-byte UTF-8 sequences (such as emojis or accented characters). The complete solution for decoding JWT parts safely in client-side JavaScript is shown below:
function decodeBase64Url(base64UrlString) {
// Step 1: Replace Base64URL characters with standard Base64 characters
let base64 = base64UrlString.replace(/-/g, '+').replace(/_/g, '/');
// Step 2: Pad the Base64 string with '=' until its length is a multiple of 4
const pad = base64.length % 4;
if (pad) {
if (pad === 1) {
throw new Error('Invalid Base64URL string structure');
}
base64 += '='.repeat(4 - pad);
}
// Step 3: Decode Base64 string into raw binary string
const binaryString = atob(base64);
// Step 4: Convert binary string to UTF-8 character representation
const bytes = Uint8Array.from(binaryString, char => char.charCodeAt(0));
const utf8Decoder = new TextDecoder('utf-8');
// Step 5: Parse and return JSON object
return JSON.parse(utf8Decoder.decode(bytes));
}
// Example usage on a JWT token segment:
const jwtHeaderSegment = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9";
const headerJson = decodeBase64Url(jwtHeaderSegment);
console.log(headerJson.alg); // "RS256"
Node.js native Base64URL parsing
In Node.js applications, `Buffer` supports the native `base64url` encoding type directly without manual character replacements:
// Node.js Base64URL decoding
const base64UrlSegment = "eyJzdWIiOiJ1c2VyXzk4NzY1NCJ9";
const jsonString = Buffer.from(base64UrlSegment, 'base64url').toString('utf-8');
const payload = JSON.parse(jsonString);
console.log(payload.sub); // "user_987654"
Security risks of third-party online JWT decoders
Developers frequently debug authentication tokens by pasting live OAuth2 tokens into public online web decoders. However, using third-party web tools introduces severe security vulnerabilities:
- Bearer Token Exposure & Session Hijacking: Bearer tokens function like temporary passwords. If an online decoder transmits your pasted token to remote servers, any party intercepting that network traffic or accessing server logs can impersonate your active user session.
- Server-Side Access Logging: Web applications that decode tokens on their servers store incoming request payloads in HTTP access logs, reverse-proxy caches, and central log aggregators. Sensitive identity claims remain stored permanently in third-party log infrastructure.
- Exposure of Internal System Identifiers: JWT payloads contain internal database primary keys, tenant IDs, email addresses, and API scopes. Transmitting these claims across third-party infrastructure violates compliance standards like GDPR, SOC 2, and HIPAA.
- Accidental Secret Key Leakage: Some web tools ask developers to paste private HMAC secret keys or RSA private keys to verify token signatures. Transmitting private keys over web forms risks total cryptographic compromise of your authentication provider.
Step-by-step: how to decode JWT tokens privately in your browser
To eliminate security risks, decode JWT tokens using client-side tools that operate 100% within your web browser memory. Follow these steps using EasyExtract’s browser-bound utility:
Step 1: Copy your raw JWT string
Copy your OAuth2 or OpenID Connect token from your application’s HTTP headers (Authorization: Bearer <token>), browser storage, or terminal logs. Ensure the string contains two separating period characters.
Step 2: Launch the EasyExtract JWT Extractor
Navigate to the private JWT Extractor. EasyExtract executes all Base64URL parsing and JSON formatting locally inside your browser DOM using JavaScript.
Step 3: Paste the token string
Paste your JWT into the input field. The browser immediately splits the token into header, payload, and signature components.
Step 4: Inspect decoded claims and expiration status
Review the formatted Header JSON, Payload JSON, and human-readable UTC expiration timestamps. Zero network calls are made during this process, keeping your access tokens completely isolated on your machine.
Frequently asked questions about JWT decoding
Can you decode a JWT without a secret key?
Yes. Base64URL encoding is a public formatting method, not encryption. Anyone who possesses a JWT string can instantly decode its header and payload claims without knowing the secret key or private key. The secret key is only required to verify or generate the cryptographic signature.
What is the difference between decoding and verifying a JWT?
Decoding parses the Base64URL strings into readable JSON objects to inspect claim values. Verifying re-calculates the signature using a secret or public key to mathematically prove that the token was issued by a trusted party and has not been modified or tampered with.
What is the difference between JWS and JWE tokens?
JWS (JSON Web Signature – RFC 7515) tokens are signed but unencrypted; their contents are visible to anyone holding the token string. JWE (JSON Web Encryption – RFC 7516) tokens encrypt the payload so that only holders of the decryption key can view the claims.
Why do JWT expiration timestamps use Unix epoch integers?
Unix epoch integers (NumericDate format) represent time compactly as seconds since January 1, 1970. Integers reduce payload size compared to ISO-8601 strings and allow fast numeric comparisons (exp > currentTime) across programming languages.
How can I check if a JWT token has expired in JavaScript?
To check if a token is expired in JavaScript, decode the payload, extract the exp claim, and compare exp * 1000 against Date.now(). If Date.now() >= exp * 1000, the token is expired.
Is it safe to store sensitive user data in a JWT payload?
No. Standard JWS payloads are Base64URL encoded and unencrypted. Never store confidential data such as passwords, credit card details, or social security numbers in a standard JWT payload.
Why does Base64URL omit equal sign (=) padding?
Base64URL omits trailing equal signs (=) because = characters require percent-encoding (%3D) when transmitted in URL query parameters or HTTP headers, which unnecessarily bloats string size.
Related reading and extraction tools
Explore related developer guides and client-side extraction tools across our network:
- EasyExtract In-Browser JWT Extractor Tool
- Text Extraction Category Hub
- How to Extract Data from HAR Files
- How to Read PEM Certificate Files
Sources and authoritative references
- IETF RFC 7519: JSON Web Token (JWT) Specification — https://datatracker.ietf.org/doc/html/rfc7519
- IETF RFC 7515: JSON Web Signature (JWS) Specification — https://datatracker.ietf.org/doc/html/rfc7515
- IETF RFC 7517: JSON Web Key (JWK) Specification — https://datatracker.ietf.org/doc/html/rfc7517
- IETF RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage — https://datatracker.ietf.org/doc/html/rfc6750
- IETF RFC 4648 §5: Base64URL Encoding Standard — https://datatracker.ietf.org/doc/html/rfc4648#section-5