Decoding a JWT token in Angular means splitting the compact token into its three Base64url segments, decoding the header and payload with window.atob, and parsing the result as JSON so the registered and custom claims become readable TypeScript objects — a process that takes a few lines of code and no external library.

Angular developers usually run into the need to decode a token when an HTTP interceptor has already attached the JWT to outgoing requests and a component, guard, or service needs to know what is inside it. A common scenario is reading the subject, roles, or expiry from the payload to decide whether to show a feature, redirect to the login page, or schedule a silent refresh. Because the header and payload are only Base64url-encoded and not encrypted, anyone who holds the token can read them in the browser. The operation is fundamentally a string transformation: split on the dot, decode the first two segments, and JSON.parse. That simplicity is also the reason decoding is never the same as validating — the signature is not checked, the issuer is not checked, and the audience is not checked, so a readable payload proves nothing about who signed the token or whether it should be trusted.

how to decode jwt token in angular
how to decode jwt token in angular

The Three Segments of a Compact JWT

A compact JWT is a string with exactly three segments separated by dots, as defined in section 7.2 of RFC 7519: header.payload.signature. When an Angular HttpInterceptor attaches a token to a request, the entire string is sent as a Bearer credential. The first segment is the header, a small JSON object that usually names the signing algorithm and the token type. The second is the payload, a JSON object with the claims the issuer chose to include. The third is the signature, the result of signing the first two segments with a key, and is the only part that proves authenticity.

Both the header and payload are encoded with the URL-safe Base64 alphabet from RFC 4648, which uses the characters A–Z, a–z, 0–9, hyphen, and underscore. Standard padding characters are removed. Because the alphabet is reversible, decoding the first two segments back to JSON does not require any secret. The third segment is treated as opaque bytes by any tool that only decodes; only a verification step that knows the signing key can confirm it.

Angular stores the token in a few typical places: an in-memory AuthService, sessionStorage, localStorage, or an HTTP-only cookie set by the backend. In the first three cases, the SPA can reach the raw string and decode it. In the last case, the JavaScript runtime never sees the token, so decoding must happen on the server or be skipped altogether.

Why Angular Code Reaches Into a Token

Decoding a payload is useful in several everyday Angular scenarios: showing the logged-in user's display name from a custom claim, checking the expiration time to decide whether to schedule a silent refresh, reading a tenant or scope claim to gate a route, or logging the decoded object while debugging an interceptor. Unit tests also benefit from being able to assert which claims a fake token carries, without pulling in a full verification stack. None of these tasks need the signature to be checked. They only need the JSON to be readable.

When a developer first lands on a Stack Overflow thread asking how to decode a JWT in Angular, the accepted answer is almost always a four-line atob snippet. That snippet is enough to read the payload. It is not, however, enough to call the token valid, and treating the two as equivalent is the most common security mistake tied to this topic. Before any production code uses the claims to make a routing or display decision, the token should also be verified by a library that knows the signing key.

The standard registered claims defined by RFC 7519 are the ones most Angular code ends up reading after decoding:

ClaimTypeMeaning
issstringIssuer — the principal that issued the token
substringSubject — the principal that is the subject of the token
audstring or arrayAudience — the recipients the token is intended for
expNumericDate (seconds)Expiration time — the time on or after which the token must not be accepted
nbfNumericDate (seconds)Not before — the time before which the token must not be accepted
iatNumericDate (seconds)Issued at — the time at which the token was issued
jtistringJWT ID — a unique identifier for the token, useful for replay prevention

NumericDate values are Unix seconds, not milliseconds. Many Angular apps confuse the two, treat an iat or exp as a millisecond timestamp, and end up with a date decades in the future or in the past. Converting exp with new Date(exp * 1000) is the right shape; dividing by 1000 first and then multiplying again is a common bug.

How to Decode a JWT Token in Angular With atob

Decoding a token in an Angular component or service is a small, well-defined sequence. The browser's window.atob does the Base64 part, and JSON.parse turns the result into an object the template can read. The atob function expects the standard Base64 alphabet, so a small substitution is needed because JWTs use the URL-safe variant. The steps below cover the full path from a stored token to a typed object ready for a template or a guard.

  1. Retrieve the token from the storage layer your auth flow uses. Read it from the AuthService signal, from sessionStorage.getItem('access_token'), or from the HttpInterceptor that already has it on the request. Confirm the value is a non-empty string before continuing.
  2. Split on the dot character and confirm the result has exactly three segments. Anything other than three segments means the input is not a compact JWT and decoding should stop with an error rather than silently returning a partial result.
  3. Translate the URL-safe alphabet to standard Base64 by replacing hyphen with plus and underscore with slash. The header and payload segments are normally unpadded, so add the right number of equals signs so that the length is a multiple of four before calling atob. This step is the most common source of InvalidCharacterError in Angular code.
  4. Decode the header and payload with atob and wrap each call in a try/catch. A malformed token will throw, and the surrounding service should treat the throw as "not a usable token" rather than crash the route.
  5. Parse each decoded segment with JSON.parse and assign the result to a typed shape. A small JwtHeader and JwtPayload interface in the auth folder makes the rest of the code type-safe and gives editors autocomplete for iss, sub, aud, exp, nbf, iat, and jti.
  6. Expose the payload through a signal or observable so the rest of the app can read it. The Angular template can render the claims directly with the built-in json pipe, which calls JSON.stringify under the hood and is a fast way to confirm the decode worked during development.
  7. Log out and remove the token from the input and from any clipboard or dev console when the inspection is done. Tokens frequently contain personal identifiers, tenant data, or scope values that should not be left in screenshots, chat threads, or shared screen recordings.

The same logic fits in a single helper service so that components and guards do not duplicate the alphabet translation. Once the helper exists, every consumer in the Angular project can call decodePayload(token) and get a typed JwtPayload back.

Use a Local JWT Decoder for Quick Inspection

When the goal is to look at the claims during development rather than to wire the result into a feature, a browser-based JWT Decoder is the faster option. Paste the compact token into the page, select the decode action, and the header and payload appear as JSON objects with the registered claims called out individually. The third segment is checked for compact shape only and is never interpreted as a successful signature, which keeps the inspection honest.

The tool runs entirely in the browser. The token does not leave the page, no signing key is fetched, and nothing is stored between visits. That makes it a safe place to inspect a token you already have authority to view, without sending it to a third-party decoder or a public API. For a deeper walkthrough of the same workflow, the guide on decoding JWT tokens without code in your browser covers the same tool from a different angle.

For claims that hold Unix seconds, a quick second pass through a Unix Timestamp Converter can confirm that the exp value lines up with the session lifetime your backend documents, without leaving the browser. JSON Formatter and JSON Validator are useful companions when the decoded payload contains long custom claim values that need to be made readable or checked for syntax.

What Decoding Will Not Tell You

Decoding a token is a string transformation, not a security check. The header tells you which algorithm the issuer claims to have used, but it does not confirm that the signature was actually produced with that algorithm. A common attack on client-side decoders is an alg-none downgrade, where the header advertises an unsecured token and the third segment is empty. The decoder will still show a perfectly valid-looking payload, because decoding never consults the third segment as a signature.

The exp, nbf, and iat values are conventions, not guarantees. A token that decodes successfully can be expired, not yet valid, issued by a party the application does not trust, intended for a different audience, or simply forged. The decoder does not fetch a JWKS endpoint, does not check the issuer against an allow list, and does not compare the audience against a configured value. The audience claim, which can be a string or an array of strings, is rendered as JSON without deciding whether it matches the application. NumericDate values are a way of writing time, not a promise that the token is currently acceptable; clock skew, refresh windows, and the issuing system's policy all sit outside the decode step.

Custom claims — the private claim names that the application layer adds on top of the registered set — are visible in the raw payload, but the decoder assigns them no special meaning. If a private claim is missing or unexpected, the decode still succeeds. The same applies to a JWE: a JSON Web Encryption token is not readable with this tool, and there is no fallback that will reveal encrypted content.

When to Validate Instead of Just Decode

Decode is for reading. Validate is for trusting. The two operations belong in different layers of an Angular application. The decoded payload is fine for showing the user's name, deciding whether to show an admin link, or branching a route based on a role claim. The moment a decision grants access to data, calls a sensitive endpoint, or skips a login screen, the token should be verified by code that holds the signing key or that has just fetched it from the authorization server.

For HS256, RS256, and ES256 tokens, a maintained library such as @auth0/angular-jwt or jose handles verification in a few lines. The library checks the algorithm against an allow list, verifies the signature against the configured key, compares iss to the expected issuer, checks aud against the expected audience, and confirms that the current time falls inside the nbf to exp window with whatever clock skew the system requires. A short-lived access token plus a long-lived refresh token, both verified on the SPA at request time and on the backend at the resource server, is the standard shape for a single-page application. The decoded payload still helps the UI render faster, but it never makes the access decision on its own.

If the application cannot verify the token at all because it has no key, the right answer is to send the token to the backend and let the resource server make the trust decision. The SPA can decode to render a loading state, and the backend can return the real authorization verdict a few milliseconds later. The two together cover the common Angular cases where a decoded payload is useful without ever being mistaken for a proof of authenticity.

Related reading: Generate a JWT for Testing: HS256 Tokens in Your Browser.