SSOGEN / Learn / Free JWT Decoder: Read, Check and Verify a Token in Your Browser

Free tool

Free JWT Decoder: Read, Check and Verify a Token in Your Browser

A JWT decoder turns the three dotted parts of a token into readable JSON. This one also explains each claim, flags the usual faults and verifies the signature with a key you paste, all on your computer.

Decode, check and verify a token here

Paste a token (a leading Bearer prefix is accepted) and select Decode. The header and claims appear in tables, with a plain-words meaning for each registered claim and times in UTC.

To verify, open Verify the signature and paste a JWKS, a single JWK, a PEM public key or, for an HS256 token, the shared secret. The tool refuses a private key.

An ID token, an access token or an agent credential: three base64url parts separated by dots. A five-part token is an encrypted JWE: add the key under Decrypt an encrypted token. A leading Bearer is fine.

Verify the signature (optional)

The sample buttons create a new key pair in your browser, sign the token with it and fill this box with the public key. Edit one character of the token and decode again to see the signature fail.

Decrypt an encrypted token (JWE)

Use test keys only. The key stays in this page and is never sent anywhere, but a production private key should not be pasted into any web page. If the decrypted content is a signed token, it is decoded below, and the key box under Verify the signature checks its signature.

Key management: dir, A128KW, A192KW, A256KW, RSA-OAEP, RSA-OAEP-256, ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW. Content: A128GCM, A192GCM, A256GCM, A128CBC-HS256, A192CBC-HS384, A256CBC-HS512. Compression: zip=DEF. Not supported: RSA1_5, PBES2-*, A128GCMKW, A192GCMKW, A256GCMKW and the JSON serialization.

Private by design. Decoding and verifying happen in this page. Nothing is sent to SSOGEN or anyone else (check your browser's network tab). A real access token is a credential: do not paste a live production token on a shared screen, and treat one you have pasted anywhere as exposed.

The two sample buttons build a working example. Load a signed Acme ID token and Load an AI agent credential each:

  • generate a new ES256 key pair in your browser;
  • sign a fictional token with the private key;
  • put the public key in the key box.

Decrypt a JWE (encrypted token)

A token with five dot-separated parts is a JWE, an encrypted token. Its payload is unreadable until the recipient’s key decrypts it.

  1. Paste the token.
  2. Open Decrypt an encrypted token (JWE).
  3. Add the key.
  4. Select Decrypt.

The protected header (alg, enc, kid, zip, cty) shows without any key. Load an encrypted sample builds a fresh A256GCM token with a random key so you can try it.

The key box takes a private JWK or key set, a PKCS#8 PEM private key (BEGIN PRIVATE KEY), or for symmetric algorithms an oct JWK or a base64url key.

Use test keys only. The key stays in this page and nothing is uploaded.

Header field Supported values
alg (key management) dir, A128KW, A192KW, A256KW, RSA-OAEP, RSA-OAEP-256, ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW
enc (content) A128GCM, A192GCM, A256GCM, A128CBC-HS256, A192CBC-HS384, A256CBC-HS512
zip DEF (DEFLATE, decompressed after decryption, 8 MiB limit)
Key types RSA, EC P-256, P-384, P-521, X25519, symmetric oct

The authentication tag is checked first. For the CBC-HS family the HMAC is verified before any decryption, as RFC 7518, section 5.2.2.2 requires. The tool then reads the plaintext by type:

  • A nested JWT (a signed token inside the encrypted one) goes straight into the decoder above, so you see its claims and checks. The key box under Verify the signature checks its signature.
  • JSON is pretty-printed, with a table for an object.
  • Other content shows as text, or as hex when it is binary.

Common failures have specific messages:

  • a wrong key type for the alg;
  • a public key where a private key is needed;
  • a key set with no matching kid;
  • an AES Key Wrap integrity failure;
  • an authentication tag mismatch, which means the key is wrong or the token was changed.

What decoding proves and what it does not

To decode a JWT, split it at the dots and base64url-decode the first two parts: the header and the claims. Decoding needs no key, so anyone can do it and it proves nothing about the token.

This JWT decoder also explains each claim, runs the checks that catch most failed sign-ins, and verifies the signature when you supply the issuer’s public key. It makes no network request.

Break a signature on purpose

  1. Load a sample and decode

    Select Load an AI agent credential, then Decode. The signature check reports a match for the key in the box.
  2. Change one character in the middle part

    Edit one letter in the second segment of the token, the one between the dots, so a claim changes slightly.
  3. Decode again

    The signature check now fails. The signature covers the exact bytes of the first two parts, so any change breaks it. This is what lets a service trust a token it did not issue.
  4. Try the wrong key

    Select Load a signed Acme ID token and paste its token with the key from the other sample. Verification fails again. A valid signature needs the key that signed it.

How to read the result

A JWT has three base64url parts joined by dots: header, claims and signature. RFC 7515, section 7.1 defines this compact form. The signature covers the first two parts exactly as written (section 2, read 2026-10-09).

The header says how the token was signed:

  • alg names the algorithm. Check it first: alg of none means unsigned, and RFC 9068 forbids that for OAuth access tokens.
  • kid hints which key was used (RFC 7515, section 4.1.4).
  • typ names the token type.

The claims carry the assertions about the subject and the token. The table shows what each common claim means and what your service must check it against. Decoding gives you the value; only the comparison protects the service.

Claim Plain meaning Check it against
iss Who issued the token The exact issuer string you configured, character for character
sub Who or what the token is about Your own account records. Often an opaque identifier, not a name
aud Who the token is for Your own identifier. Reject the token if you are not listed
exp When it stops being valid The current time, with a small allowance for clock difference
nbf When it starts being valid The current time, same allowance
iat When it was issued Used to judge age and lifetime (exp minus iat)
jti A unique token identifier A replay list, if you keep one
azp The client the token was issued to The client you expect, where aud has several values
scope What the token allows The operation being attempted
nonce A value tied to one sign-in request The nonce your application sent
cnf A key the token is bound to The proof of that key sent with the request (see DPoP below)
act The actor, when one party acts for another Whether you accept delegation at all

Mistakes that cause real incidents

  • Reading exp and stopping, without checking aud. A token issued for another service is valid, signed and still not for you.
  • Picking the verification algorithm from the token’s own header. Set the allowed algorithms in your configuration and reject any token that disagrees.
  • Accepting none, or an HS256 token where you expect an RSA or EC key.
  • Comparing iss loosely. A trailing slash is a different issuer.
  • Caching signing keys forever. The first key rotation takes the service down.
  • Treating one issuer’s sub as unique across issuers. Key your accounts on issuer and subject together.
  • Logging whole tokens. A logged token is a stored credential.

Verify the signature: where the key comes from

A signature check is only as trustworthy as the key behind it.

The issuer publishes its keys as a JWKS: a JSON object with a keys member holding an array of JWKs (RFC 7517, section 5, read 2026-10-08).

Each JWK is a public key written as JSON and can carry use (signature or encryption) and alg to restrict its purpose.

The header’s kid selects the right key from that set. RFC 7517, section 4.5 says kid is used to match a specific key, for example to choose among keys during rollover.

Key rotation depends on this:

  1. The issuer publishes a set with more than one key and signs new tokens with the new one.
  2. Tokens signed with the old key still verify until they expire.
  3. Your service fetches the set from the issuer, caches it, and fetches it again when it meets a kid it does not know.

An OpenID provider lists its JWKS address in its discovery document as jwks_uri. For SSOGEN, an OpenID Connect application’s Integration tab shows both: the discovery document at https://sso.acme.example/.well-known/openid-configuration and the keys at https://sso.acme.example/oauth2/jwks.

To check a real token against its issuer’s keys:

  1. Open the JWKS address in a browser and copy the JSON into the key box above.
  2. Paste a token the same provider issued and decode.

A match means the token is intact and a key in that set signed it.

It does not prove the JWKS came from the issuer you expect. Fetch it over HTTPS from an address in your own configuration, never from a field inside the token you are checking.

JWT expiration and clock problems

exp, nbf and iat are all NumericDate values: seconds since 1 January 1970 UTC (RFC 7519, section 2, read 2026-10-09).

Reject a token at or after exp, or before nbf.

The same RFC lets implementations allow “some small leeway, usually no more than a few minutes” for clock skew. The tool allows 60 seconds, then reports the problem.

Two machines rarely agree to the second, so these errors tend to appear after a deployment, a virtual machine pause or a change of host.

Symptom Likely cause Fix
“Token expired” straight after sign-in The service clock is ahead, or the lifetime is shorter than the work Compare iat and exp in the decoder with the service’s time. Fix the clock with time synchronization, then reconsider the lifetime
“Token not yet valid” The issuer’s clock is ahead of the service, so nbf or iat is in the future Synchronize both clocks. Add a skew allowance of tens of seconds, not hours
Works for a minute, then fails A very short lifetime, such as 60 seconds, with no refresh logic Refresh before exp, or request a new token per call
Fails only on one server That host’s clock drifted Check the host’s time source
Never expires exp is missing, or the lifetime is days or years Treat as a configuration fault. The tool warns on a lifetime over a day
Valid in the decoder, rejected by the service The service expects a different aud, iss or algorithm Compare each with the claims table above

Access token vs ID token

Both are often JWTs, but different parties read them.

ID token Access token
Who reads it The client application, to learn who signed in The API (resource server), to decide on a request
aud should be The client’s own ID The API’s identifier
Standard OpenID Connect Core, section 2 RFC 9068 when it is a JWT
Always a JWT Yes, per OpenID Connect Core No. The client must treat it as opaque

OpenID Connect Core says an ID token carries claims about the authentication of the end user and is a signed JWT. Its validation steps (section 3.1.3.7) include checking:

  • iss
  • that aud contains your client ID
  • exp
  • the signature
  • the nonce, if you sent one

The same document calls the access token opaque to the client, to be used at the UserInfo endpoint (all read 2026-10-08).

RFC 9068 defines a JWT format for access tokens, so a resource server can validate one without calling the issuer.

  • Required. typ of at+jwt and the claims iss, exp, aud, sub, client_id, iat and jti.
  • Client rule. The client must not inspect the content, because the format can change without notice.

Decode access tokens to debug, but never build client logic on what you see. The OIDC versus OAuth page explains why the two protocols produce these two tokens.

Seven things a decoder cannot tell you

  1. Whether the token is for your service. Decoding shows aud. Only you know your identifier.
  2. Whether you trust the issuer. A well-formed token from an unfamiliar issuer decodes perfectly.
  3. Whether it has been revoked. exp is a promise made at issue time. A disabled user’s token still decodes cleanly. The SSOGEN guide to service applications makes the same point: an API that checks tokens locally keeps accepting a token until it expires.
  4. Whether the scope is enough for the operation you’re about to run.
  5. Whether the sender holds the key. A token bound to a key (cnf) is safe only if each request carries proof of that key. DPoP is that proof, and a decoder never sees it.
  6. What an opaque token means. A token with no dots, or one that does not decode to JSON, is a reference. Only the issuer can tell you what it stands for, usually through introspection.
  7. What is inside an encrypted token without its key. OpenID Connect Core lets an ID token be encrypted as well as signed. Without the recipient’s key no decoder can read it. With a test key, the JWE section above decrypts it here.

How SSOGEN issues tokens

For people. An app that signs in with OpenID Connect gets an ID token and an access token at SSOGEN’s token endpoint.

  • iss. The issuer on the app’s Integration tab.
  • aud. The app’s client ID.
  • sub. SSOGEN’s stable internal identifier for the person unless you pick another attribute with Identify the person by, so do not expect a readable name there.

For services. A service application that calls an API as itself gets an access token with a lifetime you choose, 15 minutes by default. The API should verify it against the JWKS.

For AI agents. An agent’s credential lasts minutes: 240 seconds by default, never more than 270.

  • sub. The person the agent acts for.
  • act. The agent.
  • cnf.jkt. Binds the token to the agent’s key, so a copied token is useless without the key.

Paste a token like that into the tool, or use Load an AI agent credential, to see act and cnf explained. The token exchange page explains the delegation pattern.

If you are new to the protocol, start with what OpenID Connect is.

What is a JWT decoder?
It splits a token at the dots and base64url-decodes the header and claims into JSON you can read. It doesn’t check the signature unless you give it the key, and it doesn’t decide whether to trust the token.
Is it safe to paste a JWT into an online decoder?
Only if the page never sends it anywhere and the token isn’t a live credential. This page makes no network request, and you can confirm that in your browser’s Network tab. Use test tokens anyway. A live token works for anyone who holds it.
How do I check whether a JWT has expired?
Read exp, a time in seconds since 1970 UTC, and compare it with the current time. The tool shows it as a date with a relative phrase and flags an expired token, with 60 seconds of clock difference allowed.
Where do I find the JWKS for my provider?
Look in the provider’s discovery document for jwks_uri. For SSOGEN, it’s the JWKS address on the application’s Integration tab.
How do I decrypt a JWE?
A JWE has five parts and needs the recipient’s key. Paste the token, open Decrypt an encrypted token (JWE), add the private JWK, PKCS#8 PEM or shared key, and select Decrypt. The tool checks the authentication tag, decrypts in your browser and decodes a nested JWT. Use test keys only.
Can I decode a JWT without the secret or key?
Yes. The header and claims are encoded, not encrypted, so anyone can read them. You need the key only to verify the signature.

Where to go next

Sources, all read 2026-10-09: RFC 7515, RFC 7519, RFC 9068. JWE sources, read 2026-10-27: RFC 7516, RFC 7518 and RFC 7520, whose test vectors the decryption code passes.