If you've worked with REST APIs, ASP.NET Core, Node.js, React, Next.js, or modern authentication systems, you've probably encountered JWT (JSON Web Token).
A JWT often looks like a long, unreadable string:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature
But what's actually inside it, and how does JWT authentication work?
What Is JWT?
JWT stands for JSON Web Token.
It is a compact format for representing claims that can be transferred between systems. JWTs are commonly used for authentication and authorization in web applications and APIs.
A simplified authentication flow looks like this:
User
↓
Login
↓
Authentication Server
↓
JWT
↓
Client
↓
API Request + Bearer Token
↓
API Server
↓
Token Validation
↓
Protected Resource
After a successful login, the server issues a token. The client then sends that token when accessing protected resources.
For example:
Authorization: Bearer <JWT>
The API validates the token before trusting its claims.
The 3 Parts of a JWT
A typical signed JWT consists of three parts:
HEADER.PAYLOAD.SIGNATURE
Each part has a different purpose.
1. Header
The header usually contains information about the token type and signing algorithm.
{
"alg": "HS256",
"typ": "JWT"
}
For example, HS256 indicates HMAC using SHA-256.
2. Payload
The payload contains claims.
{
"sub": "12345",
"name": "John Doe",
"role": "admin",
"iat": 1789474800,
"exp": 1789478400
}
Common claims include:
-
sub— Subject -
iss— Issuer -
aud— Audience -
exp— Expiration time -
iat— Issued at -
nbf— Not before -
jti— JWT ID
Applications can also define custom claims.
3. Signature
The signature helps the server verify that the signed token has not been modified.
Conceptually, the signature is created from the encoded header and payload using a cryptographic algorithm and an appropriate secret or key.
This allows the server to detect tampering.
Is a JWT Encrypted?
Not necessarily.
This is one of the most important things to understand about JWT.
A normal signed JWT uses Base64URL encoding for its header and payload. Encoding does not make the information secret.
For example, if a JWT contains:
{
"role": "admin"
}
someone who possesses the token can generally decode that information.
Therefore, don't put passwords, secret keys, or unnecessary sensitive information inside a normal JWT payload.
Remember:
Encoding ≠ Encryption
Decoding vs Verifying a JWT
Another common misconception is that decoding a JWT means the token is valid.
It doesn't.
Decoding allows you to inspect the contents of the header and payload.
Verification checks whether the signature is valid and whether the token meets the application's validation requirements.
For example, a decoded token might contain:
{
"role": "admin"
}
That doesn't prove that the server should trust the role claim.
The server must properly validate the token before using its claims for authentication or authorization.
How to Decode a JWT
A JWT can be separated into its three components using the . character:
HEADER.PAYLOAD.SIGNATURE
The header and payload can then be Base64URL-decoded.
For example, the payload might reveal:
{
"sub": "12345",
"name": "John",
"role": "user",
"exp": 1789478400
}
You can do this manually during development, but a JWT decoder is much more convenient when debugging APIs.
If you need to inspect a token quickly, you can use the BlazeSolutions JWT Decoder to decode and inspect the JWT header and payload.
Important: Never paste production tokens containing sensitive information into an online tool unless you understand the privacy and security implications.
How to Check JWT Expiration
One of the most useful claims when debugging authentication problems is exp.
For example:
{
"sub": "12345",
"exp": 1789478400
}
The exp claim represents the token's expiration time.
If an API suddenly starts returning:
401 Unauthorized
checking the expiration claim is a useful first step.
However, an expired token isn't the only possible reason for a 401 response. Signature validation, issuer, audience, clock differences, authentication configuration, and other factors can also cause authentication failures.
JWT Access Tokens and Refresh Tokens
Modern authentication systems often use both access tokens and refresh tokens.
An access token is typically short-lived and is used to access protected APIs.
A refresh token can be used to obtain a new access token when the existing access token expires.
A simplified flow looks like this:
Login
↓
Access Token + Refresh Token
↓
Access Token Expires
↓
Refresh Token
↓
New Access Token
The exact implementation depends on the authentication architecture and protocol being used.
Common JWT Security Mistakes
JWT itself doesn't automatically make an authentication system secure. The implementation matters.
Some common mistakes include:
Trusting decoded claims
A decoded payload should not automatically be considered trustworthy.
Always perform proper token validation on the server.
Putting sensitive information in the payload
Avoid storing passwords, secret keys, or unnecessary confidential information in a normal JWT.
Using unnecessarily long-lived access tokens
If an access token is compromised, a long lifetime can increase the potential impact.
Skipping issuer and audience validation
If your application depends on iss and aud, make sure those claims are properly validated.
Accepting unexpected signing algorithms
Configure the server to accept only the algorithms appropriate for your authentication system rather than blindly trusting token-provided algorithm information.
JWT Debugging Checklist
When troubleshooting a JWT authentication problem, check:
- Token structure — Does it contain three expected sections?
- Header — Is the expected signing algorithm being used?
- Payload — Are the required claims present?
- Expiration — Has the token expired?
- Issuer — Is the token from the expected issuer?
- Audience — Is it intended for your API?
- Signature — Can the server successfully validate it?
- Authorization — Does the authenticated user have the required permissions?
This simple checklist can save a lot of time when debugging API authentication.
Final Thoughts
JWT is relatively simple once you understand its structure:
JWT
├── Header
├── Payload
└── Signature
The most important distinction to remember is:
A JWT can be decoded without being verified.
Understanding that difference helps avoid many common authentication and security mistakes.
If you want a deeper explanation of JWT structure, authentication flow, claims, expiration, access and refresh tokens, security considerations, and practical debugging, read the full guide:
JWT: How It Works, Structure, Authentication, and How to Decode a JWT
For quickly inspecting a JWT during development:
What JWT-related issue have you encountered recently — expired tokens, invalid signatures, 401 Unauthorized, or something else?
Top comments (0)