Guides

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 include HS256 (HMAC using SHA-256), RS256 (RSASSA-PKCS1-v1_5 using SHA-256), and ES256 (ECDSA using P-256 and SHA-256).
  • typ (Type): Specifies the token type, which is typically set to JWT.
  • 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

  1. Replace hyphens (-) with plus signs (+).
  2. Replace underscores (_) with forward slashes (/).
  3. 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Explore related developer guides and client-side extraction tools across our network:

Sources and authoritative references

About Md Rejon M

"Md Rejon M. is a premier Data Architecture Specialist and the visionary Lead Engineer behind EasyExtract. With over a decade of hands-on expertise in automation, web scraping, and document parsing, Rejon has dedicated his career to making data extraction fast, accessible, and secure. He designed EasyExtract’s unique serverless infrastructure, ensuring that all tools run 100% locally as client-side JavaScript within the user's browser. By engineering a framework where confidential contracts, client lists, and documents never touch an external server, Rejon has set a new standard for private-by-design utility tools. His deep knowledge of regular expressions, PDF structural layout parsing, and file archive decoding ensures the platform delivers pristine, deduplicated data without compromising user privacy.

Keep reading