JWT Does Not Encrypt Data: How to Read and Validate a Token

A string such as eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.p0W4T... looks as though encryption is hiding its contents. That is not true for the most common kind of JWT: anyone can turn the first two Base64URL segments back into JSON without knowing a secret or private key. The protection in such a token is not secrecy but the ability to detect a change to its claims.
This does not mean that every JWT consists of three readable parts. JWT defines a way to represent claims, while the related JWS and JWE standards give those claims their cryptographic form. Their distinction is practical rather than merely terminological because it determines which properties a token provides and what its recipient still needs to check.
From a 2010 draft to the JOSE family
The first JSON Web Token draft appeared in the IETF Datatracker in December 2010. Work later moved into the OAuth Working Group and became RFC 7519 in May 2015. The related JOSE specifications arrived at the same time: JWS for signatures and message authentication codes, JWE for encryption, JWK for describing keys, and JWA for naming algorithms.
JWT had a deliberately limited job. It provided a compact, URL-safe representation of claims exchanged between two parties. The specification did not define a login system, server session, or user's permissions, and it did not make an arbitrary claims set trustworthy on its own. Those meanings come from the protocol that uses the token and from the receiving application's rules.
The RFC's publication date is therefore not the beginning of the whole idea. JWT drafts were already being used by early OpenID Connect work while the JOSE set moved through standardization. The 2015 documents established a shared format and vocabulary rather than inventing every token use case at once.
JWT, JWS, and JWE solve different problems
RFC 7519 defines a JWT Claims Set as a JSON object. That object can be the payload of a JWS or the plaintext of a JWE. A JWS protects integrity and origin with a digital signature or MAC, whereas a JWE provides confidentiality and integrity for encrypted content. A signed JWT can also be encrypted when both properties are required, producing a nested JWT.
JWS Compact Serialization is the form most commonly seen in APIs and debugging tools. It has three Base64URL segments containing a protected header, payload, and signature or MAC value. When the payload is a JWT Claims Set, the first two segments can indeed be read without a key. Base64URL merely maps bytes onto an alphabet suitable for URLs; it makes no attempt to conceal information.

The illustration shows the specific HS256 case. The protected header and claims are encoded separately, then HMAC-SHA256 processes the first two segments and their separating period. Its result becomes the third segment:
JWS = base64url(header) + "." + base64url(payload) + "." + base64url(MAC)
MAC = HMAC-SHA256(base64url(header) + "." + base64url(payload), secret)
Compact JWE has a different structure with five segments: header, encrypted key, initialization vector, ciphertext, and authentication tag. Its claims cannot be recovered without a key. The familiar warning that “a JWT payload is always public” is consequently too broad; it applies to an ordinary unencrypted JWS, not to JWE.
The standard also describes an unsecured JWT with alg: none and an empty third segment. This is not a fallback to use after verification fails but a mode for an explicitly agreed context without cryptographic protection. RFC 8725 recommends that libraries reject such tokens unless the application has deliberately requested them.
A signature authenticates bytes, not the truth of every claim
With HS256, the issuer and every verifier know the same HMAC secret. This can be convenient within one trust boundary, but any recipient of that secret is technically able to mint tokens of its own. RS256, PS256, and ES256 separate those roles: the issuer signs with a private key, while services verify with the public key and do not gain signing authority.
None of these algorithms turns arbitrary content into truth. A successful cryptographic check establishes that particular bytes have not changed and that the operation was performed with the corresponding key. The application must still establish who owns that key, whether the algorithm is acceptable, and whether this token was intended for the current endpoint.
A safe validator does not choose an algorithm merely because an untrusted header names it. It starts with an allowlist from local configuration, binds the key to an expected issuer, and verifies the cryptographic operation. It then evaluates iss, aud, exp, nbf, and whatever other claims the surrounding protocol requires. ID Tokens, access tokens, and other JWTs also need distinct validation rules so that a correctly signed object of one type cannot be substituted where another is expected.
OAuth does not require JWT, while OpenID Connect requires it for ID Tokens
JWT frequently appears alongside OAuth 2.0, but the terms are not interchangeable. OAuth defines delegated access without imposing a single access-token format. An authorization server can issue an opaque string that a resource server checks through storage or introspection, or a self-contained JWT that the resource server validates locally. RFC 9068 standardizes a profile for the latter option without removing the former.
An OAuth client should treat an access token as opaque even when the current value happens to look like a JWT. Its format is a contract between the authorization server and resource server and may change; client behavior should not depend on decoding access-token claims.
OpenID Connect draws a clearer boundary. The ID Token is required to be a JWT and tells the client about the user's authentication event. The access token is for calling an API and may use a different format, while the refresh token obtains new access tokens and is not required to be a JWT either. An ID Token must not be sent to an API in place of an access token merely because the two strings can look similar.
JWT, cookies, and server sessions are not opposing choices
A cookie describes how a browser stores a small value and attaches it automatically to matching requests. JWT defines a claims structure. A server-side session means that significant state remains on the server while the client usually holds a random identifier. These mechanisms occupy different layers: a JWT can travel in an Authorization header or a cookie, and a cookie can contain either a JWT or a session ID.
Local JWT validation lets a resource server obtain claims without querying a central store, but it does not guarantee a stateless system. Immediate revocation, current authorization checks, key rotation, and replay detection can still require shared state. An opaque identifier adds a server or network lookup but makes central revocation easier and avoids disclosing claims to the client.
There is no universal browser storage location for every token. A Backend for Frontend can keep OAuth tokens on the server and give the browser a protected HttpOnly, Secure, and appropriately configured SameSite session cookie. JavaScript cannot read that cookie, but the backend still needs CSRF defenses, and XSS can issue requests as the user while malicious code remains active on the page.
When a browser application receives an access token directly, memory storage limits its lifetime but does not survive a reload. localStorage is convenient for persistence, yet any script executing in the same origin after an XSS compromise can read it. The current RFC 10017 evaluates these designs against specific attacker capabilities instead of declaring one storage API safe for every application.
Stealing a bearer token is dangerous regardless of its format
An ordinary OAuth access token is a bearer credential, so whoever possesses it can present it to a resource server. JWT versus opaque encoding does not change that property. Short lifetimes, minimum scope, precise audience restrictions, TLS, protected storage, and revocation all reduce the impact of a leak. Sender-constrained tokens additionally require proof of a key, so copying the token string alone is not sufficient for replay.
A refresh token normally lives longer and can mint new access tokens, making its loss more serious. RFC 9700 requires refresh-token rotation or sender constraint for public clients. These controls belong to OAuth security and do not follow automatically from using JWT internally.
What JWT Inspector checks
The JWT Inspector runs locally in the browser and is intended for compact JWS values with three segments. It decodes the JSON header and payload, renders numeric iat, exp, nbf, and auth_time claims as dates, and separately marks a token whose expiration has passed or whose not-before time is still in the future.
For MAC verification, the tool accepts a plain or Base64URL secret and supports HS256, HS384, and HS512. For asymmetric verification, it accepts a public PEM key and supports RS256/384/512, PS256/384/512, and ES256/384/512. Web Crypto performs every operation locally; the token and entered key are not sent to a server. The tool does not decrypt JWE, and it identifies alg: none as an unsigned mode that cannot be verified.
The tool's result is not full token validation. Decoding establishes no trust, while a successful signature result proves a match only with the key supplied by the user. Inspector does not retrieve a trusted key through metadata or JWKS and does not validate iss, aud, token type, scope, or application policy. Even a green exp badge is based on one time claim and cannot turn the input into an acceptable access token or ID Token.
For a meaningful test, use a non-production token, select a key from a trusted source, and define the expected algorithm, issuer, audience, and purpose beforehand. Inspector helps reveal the structure and check the cryptographic operation in isolation; the validator for the surrounding protocol must make the final acceptance decision.
Related tools
Frequently asked questions
Is a JWT always signed but never encrypted?
No. JWT describes a claims set that can be protected as a JWS or encrypted as a JWE. Developers most often encounter a signed or MACed compact JWS with three segments, where the header and payload are readable without a key. Compact JWE has five segments and places the claims inside its ciphertext. The standard also permits an unsecured JWT with alg=none, but an application should accept that mode only in an explicitly defined closed context.
How do decoding, signature verification, and validation differ?
Decoding only turns Base64URL into a readable header and claims. Verifying a signature or MAC establishes that the protected bytes have not changed and were processed by the holder of the relevant key. Full validation must also enforce an allowed algorithm, a trusted source for the key, issuer, audience, time limits, token type, and the rules of the receiving application.
Is an OAuth access token always a JWT?
No. OAuth does not require one access-token format: a token can be an opaque random string or a JWT. RFC 9068 defines a profile for JWT access tokens, but clients should not build behavior around reading their claims even then. In OpenID Connect, the ID Token is required to be a JWT because it carries information about the authentication event to the client.
Does JWT replace server sessions and cookies?
Not necessarily. JWT is a data format, a cookie is a browser storage and transport mechanism, and a session describes how an application manages state. A JWT can be stored in a cookie, while a cookie can instead carry a random server-side session identifier. The choice between locally validated tokens and server-managed state depends on revocation, scaling, and the threat model.
What happens when a JWT access token is stolen?
If it is an ordinary bearer access token, possession is enough to call the API whether its format is JWT or opaque. Short lifetimes, narrow scope and audience, protected storage, and revocation reduce the impact; some systems also bind a token to a sender key. Refresh tokens need stronger protection and generally use rotation or sender constraint.
Author
Alex RelFullstack Engineer
In commercial development since 2011 — over 15 years. Author of two SaaS products, TeleGo.io and LingvoHabit.com. The articles here come out of real client and product work.



