JWT Decoder & Validator→Specialized Version
🎟️

OAuth Token Decoder

Decode OAuth

Decoding happens in this tab. Nothing is sent to a server — but a JWT is a credential, so avoid pasting production tokens into any online tool.

✓Token is within its validity window

Expires: 3/17/2030, 5:46:40 PM (1294d 22h 29m from now)

HEADERAlgorithm & token type

{
  "alg": "HS256",
  "typ": "JWT"
}

PAYLOADData & claims

{
  "iss": "https://auth.example.com",
  "sub": "user_482",
  "aud": "api.example.com",
  "scope": "read:orders write:orders",
  "client_id": "abc123",
  "exp": 1900000000
}

SIGNATUREVerification hash

zj4Zn3g0mFQ1BqPnhZ8kCjWlYxTvKdRr5sXcOaLbEUw

Claims

ClaimValueMeaning
issregisteredhttps://auth.example.comIssuer — who minted the token
subregistereduser_482Subject — the user or entity it identifies
audregisteredapi.example.comAudience — who is allowed to accept it
scoperead:orders write:ordersGranted scopes
client_idabc123Custom claim
expregistered3/17/2030, 5:46:40 PMExpiration time

OAuth Token Decoder

Decode OAuth 2.0 access tokens in JWT format. Understand the claims and structure of tokens from OAuth providers like Auth0, Okta, and Azure AD.

OAuth Token Types

Token TypeFormatPurpose
Access TokenJWT or opaqueAPI authorization
ID TokenAlways JWTUser identity (OpenID Connect)
Refresh TokenUsually opaqueObtain new access tokens

Common OAuth JWT Claims

ClaimProviderDescription
issAllOAuth server URL
subAllUser identifier
audAllClient ID or API identifier
scopeMostGranted permissions
client_idMostApplication identifier
azpGoogle, KeycloakAuthorized party
emailOIDCUser email
nameOIDCUser display name

OAuth Token Decoder

``javascript function decodeOAuthToken(token) { const parts = token.split('.');

// Check if opaque token if (parts.length !== 3) { return { type: 'opaque', note: 'This is an opaque token - cannot be decoded client-side', token: token.substring(0, 20) + '...' }; }

// Decode JWT const decode = (s) => JSON.parse(atob(s.replace(/-/g, '+').replace(/_/g, '/'))); const header = decode(parts[0]); const payload = decode(parts[1]);

// Identify provider const provider = identifyProvider(payload.iss);

// Analyze scopes const scopes = payload.scope ? payload.scope.split(' ') : [];

return { type: 'jwt', provider, header, payload, scopes, audience: Array.isArray(payload.aud) ? payload.aud : [payload.aud], expiresAt: payload.exp ? new Date(payload.exp * 1000) : null }; }

function identifyProvider(issuer) { if (!issuer) return 'unknown'; if (issuer.includes('auth0.com')) return 'Auth0'; if (issuer.includes('okta.com')) return 'Okta'; if (issuer.includes('login.microsoftonline.com')) return 'Azure AD'; if (issuer.includes('accounts.google.com')) return 'Google'; if (issuer.includes('cognito-idp')) return 'AWS Cognito'; return 'custom'; } `

Provider-Specific Claims

ProviderUnique Claims
Auth0permissions, org_id
Azure ADoid, tid, upn
Googlehd (hosted domain), azp
Oktagroups, cid
Cognitocognito:groups, cognito:username

The Attacks a Verifier Must Stop

alg: none. A token declaring no algorithm with an empty signature. A library that trusts the header's alg will accept it as valid. Always specify the expected algorithm when verifying rather than reading it from the token.

Algorithm confusion. A token signed with HMAC using the server's *public* RSA key as the secret. If the verifier picks the algorithm from the header, an RS256 verifier can be tricked into running HS256 with a key the attacker already has.

Weak secrets. HS256 with a short or dictionary secret is brute-forceable offline from a single captured token. Use at least 256 bits of real entropy.

No expiry check. exp is a claim, not an enforcement. A library that decodes without verifying, or code that reads claims from a decoded-but-unverified token, accepts expired and forged tokens alike.

`javascript // Specify the algorithm; never trust the header's jwt.verify(token, secret, { algorithms: ['HS256'] }); `

Anyone Can Read the Payload

A JWT is signed, not encrypted. The payload is base64url — readable by anyone holding the token, including the browser it is stored in. Never put anything in it you would not print on a postcard: no passwords, no PII beyond an identifier, no internal keys.

Revocation Is the Hard Part

A signed token is valid until it expires, and there is no way to withdraw one. Options:

ApproachCost
Short expiry (5–15 min) plus refresh tokensStandard; adds a refresh endpoint
A denylist of revoked JTIsReintroduces the state JWTs were meant to avoid
Rotate the signing keyRevokes every token at once
Version claim checked against the user recordA database read per request
If you need immediate revocation for every session, a server-side session is a simpler and more honest choice than a JWT.

Where to Store One

LocationXSS-safeCSRF-safe
localStorageNoYes
sessionStorage`NoYes
Cookie (HttpOnly, Secure, SameSite)YesYes, with SameSite
In-memoryYesYes
An HttpOnly cookie is the safest default. Any token reachable from JavaScript is reachable by any script that gets injected.

Frequently Asked Questions

Can I always decode an OAuth access token?

No. OAuth tokens can be JWTs (decodable) or opaque strings (server-only). Many providers use opaque access tokens that require server introspection. ID tokens (OpenID Connect) are always JWTs and decodable. Check your provider's documentation for token format.

What is the difference between access token and ID token?

Access tokens authorize API requests—they say what you can do. ID tokens identify the user—they say who you are (OIDC). Access tokens go to resource servers (APIs). ID tokens stay with the client for user info. Never send ID tokens to APIs; use access tokens.

How do I verify an OAuth JWT token?

Fetch the provider's JWKS (JSON Web Key Set) from their well-known endpoint (/.well-known/jwks.json). Use the kid header to find the correct key. Verify the signature using that public key. Also validate iss, aud, exp, and other claims match your expectations.

Related Tools

Explore other tools you might find useful:

More JWT Decoder & Validator tools

You might also need