September 7, 2026
Testing JWT Authentication in Modern APIs: A Practical Security Approach
Introduction
By Chimezirim Oti
9 min read
Introduction
JSON Web Tokens (JWTs) are widely used by modern applications to represent authentication and authorization information between clients and APIs.
They are commonly seen in applications that use REST APIs, single-page applications, mobile applications, and distributed services.
JWTs can be useful because they allow applications to securely transmit claims between parties. However, using JWTs does not automatically make an authentication system secure.
The security of a JWT-based authentication system depends heavily on how tokens are generated, signed, validated, stored, and handled throughout their lifecycle.
From a security testing perspective, I am less interested in simply confirming that an application uses JWTs and more interested in understanding how the application validates and trusts those tokens.
This article presents a practical approach to testing JWT authentication in APIs, including token structure, claim validation, expiration, signature verification, token manipulation, and common implementation weaknesses.
What is a JWT?
A JSON Web Token (JWT) is a compact, URL-safe format used to represent claims that can be transferred between parties.
A typical JWT consists of three parts separated by periods:
Header.Payload.SignatureHeader.Payload.SignatureFor example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NSIsImV4cCI6MTcyMDAwMDAwMH0
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5ceyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NSIsImV4cCI6MTcyMDAwMDAwMH0
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cThese three components are:
- Header
- Payload
- Signature
The JWT specification defines registered claims such as iss, sub, aud, exp, nbf, iat, and jti.
Understanding the JWT Structure
1. Header
The header contains information about the token, including the cryptographic algorithm used.
For example:
{
"alg": "HS256",
"typ": "JWT"
}{
"alg": "HS256",
"typ": "JWT"
}The alg value indicates the algorithm used to protect the token.
The typ value identifies the type of token.
During testing, the header is worth inspecting because the server should have a clearly defined and securely configured set of algorithms that it accepts.
2. Payload
The payload contains the JWT claims.
For example:
{
"sub": "12345",
"role": "user",
"iat": 1720000000,
"exp": 1720003600
}{
"sub": "12345",
"role": "user",
"iat": 1720000000,
"exp": 1720003600
}The payload may contain information such as:
- User identifier
- Role
- Issuer
- Audience
- Issued time
- Expiration time
- Token identifier
It is important to understand that a normal signed JWT is not encrypted simply because it is a JWT.
The payload can generally be decoded and read.
Therefore, sensitive information such as passwords, API keys, private data, or other secrets should not be placed inside a JWT merely because the token is signed.
OWASP's JWT testing guidance specifically recommends checking whether JWTs expose sensitive information.
3. Signature
The signature provides integrity protection for a signed JWT.
Conceptually, the signature is created using the encoded header and payload together with a cryptographic key.
Header
+
Payload
โ
Signing Algorithm + Key
โ
SignatureHeader
+
Payload
โ
Signing Algorithm + Key
โ
SignatureWhen the API receives the token, it should verify the signature before trusting the claims contained in the token.
This is critical.
If an attacker modifies the payload:
{
"sub": "12345",
"role": "user"
}{
"sub": "12345",
"role": "user"
}to:
{
"sub": "12345",
"role": "admin"
}{
"sub": "12345",
"role": "admin"
}the modified token should fail signature verification unless the attacker possesses the appropriate signing key.
Authentication Flow with JWT
A simplified JWT authentication flow looks like this:
User
โ
Login
โ
Authentication Server
โ
JWT Issued
โ
Client
โ
API Request + JWT
โ
API Validates Token
โ
Access Granted or DeniedUser
โ
Login
โ
Authentication Server
โ
JWT Issued
โ
Client
โ
API Request + JWT
โ
API Validates Token
โ
Access Granted or DeniedThe important part for a security tester is what happens between:
API Request + JWT
and
Access Granted
The API should validate the token before trusting the identity or claims contained within it.
What I Look For When Testing JWT Authentication
When assessing a JWT-based API, I generally want to answer several questions:
- Is the token properly signed?
- Is the signature actually verified?
- Is the expected signing algorithm enforced?
- Is the token expiration checked?
- Is the issuer validated where appropriate?
- Is the audience validated where appropriate?
- Can the token be modified without invalidating it?
- Does the application expose sensitive information in the payload?
- Can expired tokens still be used?
- Are tokens unnecessarily long-lived?
- Can a token from one context be used in another?
- Are refresh tokens handled securely?
These checks help determine whether the application is actually validating JWTs securely rather than simply decoding them.
Testing JWT Claims
JWT claims can provide useful information about how the application manages authentication.
Consider:
{
"iss": "https://auth.example.com",
"sub": "12345",
"aud": "api.example.com",
"iat": 1720000000,
"exp": 1720003600
}{
"iss": "https://auth.example.com",
"sub": "12345",
"aud": "api.example.com",
"iat": 1720000000,
"exp": 1720003600
}iss โ Issuer
The iss claim identifies who issued the token.
A secure implementation should validate the expected issuer when the application relies on this claim.
sub โ Subject
The sub claim identifies the principal that the token represents.
For example:
{
"sub": "12345"
}{
"sub": "12345"
}The application may use this value to identify the authenticated user.
aud โ Audience
The aud claim identifies the intended recipient or audience of the token.
An API should not blindly accept a token simply because its signature is valid if the token was issued for a different audience.
exp โ Expiration
The exp claim defines when the token expires.
The server should reject a token once it has passed its expiration time.
nbf โ Not Before
The nbf claim defines the time before which a token should not be accepted.
iat โ Issued At
The iat claim identifies when the token was issued.
jti โ JWT ID
The jti claim can provide a unique identifier for a token and can be useful in token management and revocation scenarios.
These claims are defined by the JWT specification, although not every claim is mandatory for every application.
Testing Token Expiration
Token lifetime is an important part of JWT security.
Suppose an API issues a token with an expiration time:
{
"exp": 1720003600
}{
"exp": 1720003600
}A useful security test is to determine whether the API actually rejects the token after it expires.
The basic process is:
Obtain valid token
โ
Record expiration time
โ
Wait until token expires
โ
Replay the same request
โ
Observe API responseObtain valid token
โ
Record expiration time
โ
Wait until token expires
โ
Replay the same request
โ
Observe API responseA secure implementation should reject an expired token.
OWASP also recommends testing whether access-token lifetime is properly enforced when JWTs are used as access tokens.
The appropriate lifetime depends on the application's risk and architecture. There is no single expiration period that is correct for every application.
\Testing Token Tampering
One of the most important JWT security tests is determining whether modifying token claims causes the server to reject the token.
For example, imagine a token contains:
{
"sub": "12345",
"role": "user"
}{
"sub": "12345",
"role": "user"
}A tester can modify the claim in a controlled environment:
{
"sub": "12345",
"role": "admin"
}{
"sub": "12345",
"role": "admin"
}The modified token should not be accepted unless it has been correctly signed using the appropriate key.
The important question is:
Does the server verify the signature before trusting the modified claim?
OWASP's testing guidance identifies JWT tampering and modification as key areas of testing.
Testing the Signing Algorithm
The JWT header contains an alg value that identifies the cryptographic algorithm.
For example:
{
"alg": "HS256",
"typ": "JWT"
}{
"alg": "HS256",
"typ": "JWT"
}A common security concern is improper handling of the algorithm specified in the token.
The application should not blindly trust an attacker-controlled algorithm declaration.
Instead, the server should define and enforce the algorithms and keys that are valid for the particular token type and authentication flow.
The historical alg: none issue is a useful example of why this matters. JWT specifications permit unsecured JWTs in specific contexts, but an application that expects signed authentication tokens should not accidentally accept an unsigned token.
Testing Token Reuse
Another useful test is determining whether a previously issued token can continue to access protected resources after the circumstances that should invalidate it.
For example:
Login
โ
JWT issued
โ
Access API
โ
Logout / Session termination
โ
Replay JWTLogin
โ
JWT issued
โ
Access API
โ
Logout / Session termination
โ
Replay JWTThe expected behavior depends on the application's session architecture.
A JWT can remain cryptographically valid until its expiration unless the application implements a mechanism for earlier revocation.
This is an important distinction between token validity and current session state.
OWASP notes that JWT-based sessions can become disconnected from current session state, particularly when a token remains valid after an explicit logout or earlier session termination. Applications requiring immediate invalidation may need additional token-revocation mechanisms.
Testing for Sensitive Information
Because JWT payloads are typically readable by anyone who possesses the token, testers should inspect the payload for information that should not be exposed.
For example, a poorly designed token might contain:
{
"username": "user@example.com",
"role": "admin",
"password": "********",
"api_key": "********"
}{
"username": "user@example.com",
"role": "admin",
"password": "********",
"api_key": "********"
}Sensitive secrets should not be placed in a JWT payload simply because the token is signed.
A signature protects integrity; it does not make the payload confidential.
If confidentiality is actually required, an appropriate encryption mechanism must be considered.
Access Token vs Refresh Token
Modern authentication systems may use both access tokens and refresh tokens.
An access token is generally used to access protected resources.
A refresh token can be used to obtain a new access token without requiring the user to authenticate again.
A simplified flow is:
Login
โ
Access Token + Refresh Token
โ
Access Token โ API
โ
Access Token Expires
โ
Refresh Token โ New Access TokenLogin
โ
Access Token + Refresh Token
โ
Access Token โ API
โ
Access Token Expires
โ
Refresh Token โ New Access TokenThese tokens have different security considerations.
Access tokens should generally have a limited lifetime appropriate to the application's risk.
Refresh tokens typically require stronger protection because they can be used to obtain additional access tokens.
OWASP recommends short-lived access tokens and discusses secure handling and rotation of refresh tokens in OAuth-based systems.
Testing JWTs with Burp Suite
Burp Suite can be useful when testing JWT-based APIs because it allows requests to be intercepted, inspected, modified, and replayed in an authorized testing environment.
A basic workflow is:
Login
โ
Capture API request
โ
Identify JWT
โ
Send request to Repeater
โ
Inspect token
โ
Test relevant claims
โ
Replay request
โ
Compare responsesLogin
โ
Capture API request
โ
Identify JWT
โ
Send request to Repeater
โ
Inspect token
โ
Test relevant claims
โ
Replay request
โ
Compare responsesFor example, a request might contain:
GET /api/v1/profile HTTP/1.1
Host: api.example.test
Authorization: Bearer <ACCESS_TOKEN>GET /api/v1/profile HTTP/1.1
Host: api.example.test
Authorization: Bearer <ACCESS_TOKEN>The token can then be inspected to understand:
- Which claims are present
- How long the token is valid
- Which issuer created it
- Which audience it targets
- Which signing algorithm is being used
The goal is not simply to decode the token.
The goal is to determine whether the API correctly validates everything it relies upon.
Common JWT Implementation Weaknesses
Some common issues I look for include:
1. Missing signature verification
The API accepts claims without properly verifying the token's integrity.
2. Improper algorithm validation
The server does not properly restrict or validate the algorithms it accepts.
3. Missing expiration validation
Expired tokens continue to provide access.
4. Incorrect issuer validation
The API accepts tokens from an issuer it does not trust.
5. Incorrect audience validation
A token intended for another service is accepted by the API.
6. Sensitive information in the payload
Secrets or unnecessary sensitive information are placed inside the token.
7. Excessively long token lifetimes
A compromised token remains useful for an unnecessarily long period.
8. Poor token storage
Tokens are stored in ways that unnecessarily increase the impact of client-side attacks.
9. Insecure refresh-token handling
Refresh tokens are not adequately protected, rotated, or invalidated where the architecture requires it.
JWT Does Not Replace Authorization
There is an important connection between JWT security and my previous article on BOLA.
A JWT can tell an application who the user is, but that does not automatically determine which resources the user is allowed to access.
For example:
JWT
โ
User A
โ
Authenticated
โ
GET /api/orders/1002
โ
Authorization Check
โ
Does User A have access to Order 1002?JWT
โ
User A
โ
Authenticated
โ
GET /api/orders/1002
โ
Authorization Check
โ
Does User A have access to Order 1002?The API still needs to perform the authorization decision.
This is why a secure API needs both:
Authentication
Who is the user?
and
Authorization
What is the user allowed to access?
A properly signed JWT does not eliminate the need for object-level authorization.
A Practical JWT Testing Checklist
When assessing JWT authentication in an authorized environment, the following checklist provides a useful starting point:
- Identify where JWTs are used.
- Inspect the JWT header.
- Inspect the JWT payload.
- Identify the signing algorithm.
- Check whether sensitive information is exposed.
- Review relevant claims such as
iss,sub,aud,exp, andnbf. - Test whether expired tokens are rejected.
- Test whether modified claims invalidate the token.
- Verify that the server properly validates the signature.
- Check whether the expected signing algorithms are enforced.
- Test token reuse where the application's session model makes this relevant.
- Review access-token lifetime.
- Review refresh-token handling where applicable.
- Verify that JWT authentication is followed by proper authorization checks.
- Document the security impact of any weaknesses discovered.
Key Takeaways
JWTs are a useful mechanism for representing claims between systems, but the presence of a JWT does not automatically make an API secure.
When testing JWT authentication, the important questions are:
Is the token authentic?
Has the token been modified?
Is it still valid?
Was it issued by a trusted source?
Is it intended for this API?
Does the authenticated user have permission to perform the requested action?
The last question is particularly important because authentication and authorization are separate security controls.
A secure JWT implementation should therefore combine cryptographic validation, appropriate claim validation, sensible token lifetimes, secure token handling, and strong authorization controls.
JWT security is not simply about decoding a token and checking what is inside it.
It is about understanding why the application trusts the token and whether that trust is being enforced correctly.
Conclusion
JWT-based authentication is now common across modern APIs, but implementing JWTs securely requires more than generating a token and placing it in an Authorization header.
From a security testing perspective, the objective is to understand the complete authentication flow and identify where the application makes trust decisions.
A structured approach can be summarized as:
Identify JWT
โ
Inspect Header
โ
Inspect Claims
โ
Review Algorithm
โ
Test Signature Validation
โ
Test Expiration
โ
Test Relevant Claims
โ
Review Token Lifecycle
โ
Verify Authorization
โ
Assess ImpactIdentify JWT
โ
Inspect Header
โ
Inspect Claims
โ
Review Algorithm
โ
Test Signature Validation
โ
Test Expiration
โ
Test Relevant Claims
โ
Review Token Lifecycle
โ
Verify Authorization
โ
Assess ImpactThe strongest JWT implementations are not simply those that use strong cryptography.
They are systems where the application carefully validates the token, limits what the token can be trusted for, and still performs proper authorization before allowing access to protected resources.
References
- RFC 7519 โ JSON Web Token (JWT), Internet Engineering Task Force.
- OWASP Web Security Testing Guide โ Testing JSON Web Tokens.
- OWASP REST Security Cheat Sheet โ JWT and Access Control.
- OWASP Web Security Testing Guide โ OAuth Authorization Server Weaknesses.
About the Author
Chimezirim Oti is a cybersecurity practitioner focused on API security, application security, vulnerability management, and secure software development practices.
His interests include practical security assessment, API security testing, vulnerability identification, and helping organizations improve the security of their applications and infrastructure.