GUIDE / security
How to Test JWT Authentication
Learn how to test JWT authentication: claims, expiry, signatures, refresh tokens, algorithm attacks, and a practical JWT security test checklist.
A login that returns a token is only the start of auth risk on modern APIs and SPAs. How to test JWT authentication means proving the server rejects bad tokens, enforces claims, rotates refresh credentials safely, and blocks privilege escalation through payload edits.
This guide shows how to test JWT authentication with practical cases for signatures, expiry, audience, roles, refresh flows, revocation, and common anti-patterns. You will get checklists, tables, safe staging methods, common mistakes, and defect examples you can reuse.
For broader auth coverage, read API authentication testing and authentication and authorization testing. For endpoint-level security packing, use the API security testing checklist.
Ethics and Scope
- Test only systems you are authorized to test.
- Prefer staging with synthetic users.
- Do not attack production identity providers without explicit permission.
- Do not brute force secrets.
- Treat stolen or leaked real user tokens as incidents, not fixtures.
JWT Basics for Testers
A JWT often looks like three base64url sections:
header.payload.signature
Example structure (illustrative):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyXzEyMyIsInJvbGUiOiJ1c2VyIiwiZXhwIjoxNzAwMDAwMDAwfQ.
SIGNATURE
- Header: algorithm and type metadata.
- Payload: claims such as subject, roles, expiry.
- Signature: integrity protection created with a secret or private key.
Critical tester insight: readable payload is not trusted data. If the server does not verify the signature and claims, an attacker can edit the payload.
What "Good" JWT Authentication Looks Like
From a QA perspective, a solid implementation usually:
- Issues tokens only after successful authentication.
- Signs tokens with a strong algorithm and managed keys.
- Validates signature on every protected request.
- Enforces
exp, andnbfwhen used. - Validates
issandaudwhen those claims are part of design. - Treats roles and tenants as server-enforced authorization inputs, still checked against policy.
- Uses short-lived access tokens plus carefully handled refresh tokens.
- Rejects malformed tokens consistently.
- Provides a revocation or rotation story for logout, password change, and compromise.
- Avoids putting sensitive secrets in the payload.
Your tests should try to falsify each of those properties.
Tools You Need
- API client: Postman, Insomnia, Bruno, or curl.
- JWT decoder for inspection (devtools, jwt.io style tools offline, or CLI). Prefer offline decoding for sensitive staging tokens.
- Proxy: Burp or ZAP for intercepting client traffic.
- Two or more test users and at least two roles.
- Clock control awareness (waiting for expiry, or staging config with short TTLs).
You do not need to implement cryptography. You need disciplined request variants.
Step by Step: How to Test JWT Authentication
Step 1: Capture a valid token the legitimate way
- Call the real login or OAuth token endpoint.
- Store access token and refresh token separately.
- Call a protected endpoint and confirm success.
- Record response times and status codes as baseline.
Document:
- Token endpoint
- Grant type
- Client IDs if any
- Where the client stores tokens
- Access token TTL
- Refresh token TTL
Step 2: Decode and inventory claims
Decode payload and list claims:
| Claim | Example | What to test |
|---|---|---|
| sub | user_123 | Token acts only for that subject |
| exp | unix timestamp | Expired token rejected |
| nbf | unix timestamp | Early token rejected if enforced |
| iat | issued at | Sanity and lifecycle |
| iss | https://auth.example.com | Wrong issuer rejected |
| aud | api.example.com | Wrong audience rejected |
| role / roles | user | Client edits do not elevate |
| tenant_id | org_9 | Cross-tenant use denied |
| jti | unique id | Revocation/replay support if used |
Write down which claims your product documentation says are enforced.
Step 3: Missing and malformed token tests
Against a protected route:
| Case | Request change | Expected |
|---|---|---|
| No Authorization header | omit header | 401 |
| Empty bearer | Authorization: Bearer | 401 |
| Random string | Bearer not-a-jwt | 401 |
| Only two segments | remove signature part | 401 |
| Truncated token | cut last characters | 401 |
| Wrong prefix | Token abc instead of Bearer if Bearer required | 401 |
Consistency matters. Alternating 401 and 500 can leak implementation fragility.
Step 4: Signature tampering tests
- Decode payload.
- Change a claim, for example
"role": "admin". - Re-encode payload without a valid resign (or keep old signature).
- Send to API.
Expected: reject. If accepted, you likely found a critical failure: server trusting unsigned or poorly verified tokens.
Also try:
- Flip one character in the signature.
- Replace signature with signature from another token.
- Mix header from token A with payload from token B.
Step 5: Algorithm confusion and alg=none style probes
Historical classes of bugs include accepting alg values the server should not trust.
QA-friendly probes (authorized staging only):
- Create a token header with
"alg":"none"and a payload that elevates privileges, with empty signature segment as applicable to your tooling. - Send to API.
- Confirm rejection.
If your stack uses asymmetric algorithms, security teams may also test key confusion cases. QA should at least confirm algorithm is not client-controlled in a way that disables verification. If unsure, file a question to security engineering rather than inventing exotic crypto attacks.
Step 6: Expiry and not-before enforcement
Practical methods:
- Prefer staging config with short-lived access tokens (for example 60-120 seconds) for testability.
- Obtain token, wait for expiry, retry protected call.
- Confirm 401 after expiry.
- Confirm refresh flow can mint a new access token when refresh is still valid.
- If
nbfis present in future, confirm early use fails when enforced.
Avoid relying on local laptop clock tricks as your only method. Server time is the authority.
Step 7: Issuer and audience enforcement
If tokens include iss and aud:
- Capture a token intended for another environment or API if your org has multiple (dev vs staging) and testing is allowed across them carefully.
- Replay a token from service A against service B.
- Expect rejection when audiences differ.
In microservices, a token meant for the public API should not automatically authorize internal admin services unless designed that way.
Step 8: Authorization claims are not the whole story
Even a valid JWT with "role":"user" must still pass object-level checks.
Tests:
- Valid user token cannot call admin route.
- Valid user token cannot modify another user's resource.
- Editing the JWT role claim to admin fails signature checks and is rejected.
- Server does not "only" trust the token role when a database role is the source of truth, depending on architecture.
JWT authn proves identity (or session of identity). Authz still needs policy. Connect this with authentication and authorization testing.
Step 9: Refresh token lifecycle
Refresh tokens are often more valuable than access tokens because they last longer.
Checklist:
[ ] Valid refresh returns new access token
[ ] Refresh with access token rejected if wrong token type
[ ] Refresh for user A cannot be used to obtain tokens for user B
[ ] Expired refresh rejected
[ ] Revoked refresh rejected after logout
[ ] Password change invalidates refresh tokens if policy says so
[ ] Refresh rotation: old refresh rejected after use when rotation enabled
[ ] Concurrent refresh behavior understood (race may issue multiple, or reuse detection)
[ ] Refresh endpoint rate limited
Worked flow:
- Login -> access1, refresh1.
- Use refresh1 -> access2, refresh2 (if rotating).
- Replay refresh1 -> should fail if one-time rotation is required.
- Logout or revoke -> refresh2 fails.
Step 10: Logout, revocation, and password change
Designs differ:
- Stateless JWT until expiry, with short TTL.
- Denylist for
jtiuntil exp. - Versioned password stamp embedded in token.
- Server-side session store behind the JWT.
QA must test the stated design, not an imaginary ideal.
Cases:
- After logout, access token fails immediately if promised.
- If access token remains valid until exp by design, confirm TTL is short and refresh is revoked.
- After password reset, old tokens cannot continue indefinitely if policy requires kill sessions.
Document actual behavior in the bug or test notes so product and security can accept or reject residual risk.
Step 11: Transport and storage observations
While testing clients:
- Confirm tokens are not placed in URL query strings.
- Confirm browser storage choices match architecture and that CSRF controls exist if cookies are used.
- Confirm logs and error reporters do not print full tokens.
- Confirm HTTPS is required in deployed environments.
Step 12: Cross-service and partner tokens
If partners use JWTs:
- Validate partner tokens cannot call end-user routes beyond scope.
- Validate scope claims (
scope,permissions) are enforced. - Validate client credentials tokens are not overly privileged.
JWT Test Case Catalog
| ID | Title | Steps | Expected | Priority |
|---|---|---|---|---|
| JWT-01 | Valid access token succeeds | Login, call protected GET | 200 and correct data | High |
| JWT-02 | Missing token fails | Call without Authorization | 401 | High |
| JWT-03 | Tampered payload fails | Edit claim keep old sig | 401 | Critical |
| JWT-04 | Expired token fails | Wait/use expired token | 401 | High |
| JWT-05 | Wrong audience fails | Use token for other API | 401 | High |
| JWT-06 | User cannot call admin | Valid user token on admin route | 403 | High |
| JWT-07 | Refresh issues new access | POST refresh with valid refresh | New access token works | High |
| JWT-08 | Reused rotated refresh fails | Refresh twice with same token when rotation on | Second fails | High |
| JWT-09 | Logout revokes refresh | Logout then refresh | Fail | High |
| JWT-10 | alg none rejected | Send none-alg forged token | 401 | Critical |
| JWT-11 | Token in query rejected or avoided | Attempt access via ?token= | Not accepted or not used by clients | Medium |
| JWT-12 | Disabled user token fails | Disable user, reuse token | Fail per policy | High |
Example curl Patterns
Valid call:
curl -s -o /dev/null -w "%{http_code}\n" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
https://api.staging.example.com/v1/me
Missing token:
curl -s -o /dev/null -w "%{http_code}\n" \
https://api.staging.example.com/v1/me
Refresh:
curl -s -X POST https://api.staging.example.com/oauth/token \
-H "Content-Type: application/json" \
-d '{"grant_type":"refresh_token","refresh_token":"'"$REFRESH_TOKEN"'"}'
Use environment variables so tokens are not pasted into shared chat logs.
Comparing Access Tokens and Refresh Tokens
| Aspect | Access token | Refresh token |
|---|---|---|
| Purpose | Authorize API calls | Obtain new access tokens |
| Lifetime | Short | Longer |
| Exposure risk | High volume use | Higher value if stolen |
| Typical storage | Memory/cookie depending on design | More protected handling |
| QA focus | Signature, claims, expiry, authz | Rotation, revocation, binding, rate limits |
Common JWT Implementation Pitfalls QA Can Catch
- Trusting payload without verifying signature.
- Long-lived access tokens with no revocation story.
- Putting passwords or secrets in claims.
- Using the same token for all audiences.
- Role elevation via client-side claim edits accepted.
- Refresh tokens never rotated or never revoked on password change.
- Detailed error messages that help attackers (
invalid signaturevsuser disabledinconsistencies may be OK, but stack traces are not). - Tokens logged in APM tools and support dashboards.
How to File a JWT Security Defect
Title: API accepts access tokens with tampered role claim
Environment: staging API v1
Baseline: valid user token returns role user on /v1/me
Steps:
1. Capture access token for user qa_jwt_1.
2. Modify payload role from user to admin without valid re-signing (old signature retained).
3. Call GET /v1/admin/users with modified token.
Actual: 200 and admin user list returned.
Expected: 401 due to invalid signature, or at minimum 403 with no admin data and no trust of client-edited claims.
Impact: Anyone who can obtain any valid token format knowledge may forge admin privileges if signature checks are bypassed; critical auth failure.
Evidence: sanitized tokens redacted to first/last 6 chars, response bodies, timestamps.
Never paste full production tokens into tickets that broad audiences can read.
Common Mistakes When Teams Test JWT Auth
Mistake 1: Only testing login success
A 200 on login proves little about protected route enforcement.
Mistake 2: Decoding tokens and stopping
Readable claims are not validation.
Mistake 3: One role, one user
You will miss vertical privilege problems and subject mismatches.
Mistake 4: Ignoring refresh endpoints
Attackers love refresh grants because they extend sessions.
Mistake 5: No short TTL environment
If access tokens last 24 hours, expiry tests become painful and get skipped. Ask for test-friendly TTLs in staging.
Mistake 6: Treating 403 and 401 as interchangeable without documenting design
They mean different things. Test for the intended contract and keep it consistent.
Mistake 7: Forgetting mobile and old app clients
Old clients may send legacy token headers. Include them in regression if still supported.
Mistake 8: No automation for the boring denials
Missing, expired, and tampered token cases are perfect candidates for CI.
JWT Security Testing Checklist
[ ] Valid token works on intended routes
[ ] Missing/malformed tokens rejected
[ ] Tampered payload rejected
[ ] Tampered signature rejected
[ ] Expired token rejected
[ ] nbf enforced if used
[ ] iss/aud enforced if used
[ ] alg none / unexpected alg rejected
[ ] Role and tenant claims cannot be self-elevated
[ ] Object-level authz still enforced with valid token
[ ] Refresh succeeds when valid
[ ] Refresh fails when expired/revoked/reused (per design)
[ ] Logout/password change behavior matches policy
[ ] Tokens not sent in URLs by official clients
[ ] Tokens not logged in app errors
[ ] Rate limits on login and refresh
[ ] Cross-env token reuse rejected
[ ] Partner scopes enforced
How JWT Testing Fits the Wider Security Program
JWT tests are one slice of API auth. Still test:
- CSRF if cookies store tokens.
- XSS impact on token theft for browser storage models.
- Account recovery flows that issue new tokens.
- Broken access control on IDs after authentication succeeds.
See security testing for QA for the broader map, and the API security testing checklist for endpoint packing.
Practice Plan for a Team Workshop
One half-day workshop:
- Map token issuance in your staging app.
- Build the twelve core cases from the catalog.
- Automate the five fastest denials in CI.
- Review one real defect report format.
- Decide staging TTLs with backend engineers.
For ongoing skill building in competitive QA scenarios, register at QABattle sign-up and use API-focused practice in battles.
End-to-End Scenario: Login to Privileged Action
Walk a full lifecycle in staging:
- Login as standard user; store access and refresh tokens.
- Call
/v1/mesuccessfully. - Call
/v1/admin/metricsand expect denial. - Tamper with role claim; expect rejection.
- Wait for access expiry; expect
/v1/medenial. - Refresh; expect new access token success on
/v1/me. - Change password as the user.
- Confirm old refresh fails if policy requires session kill.
- Login again; confirm new tokens work.
- Logout; confirm refresh fails.
This single scenario often surfaces more design truths than ten isolated payload edits.
Service Accounts and Machine Tokens
Not all JWTs belong to humans.
Machine token checks:
[ ] Client credentials token cannot perform end-user-only actions
[ ] Scopes are minimized for each service
[ ] Token TTL is appropriate for machine use
[ ] Audience restricted to intended services
[ ] Compromised client secret rotation process exists and is testable in staging
A service token with scope=* is a future incident report.
Mobile App Specific JWT Checks
Mobile clients introduce storage and refresh edge cases.
Check:
- Tokens are not logged by debug builds shipped to testers accidentally.
- App uses official refresh flow, not home-grown insecure storage of passwords.
- Certificate pinning discussions are handled by mobile security owners; QA still verifies traffic against staging with approved tools.
- App handles 401 by refresh-then-retry without dropping users into endless loops.
- After logout, tokens are cleared from local storage/keychain equivalents.
Microservices Validation Consistency
In multi-service systems, one gateway may validate JWT while an internal service trusts headers blindly.
Risk pattern:
- Gateway checks signature.
- Gateway forwards
X-User-Idto internal services. - Internal services are also reachable through another path that does not validate.
QA and AppSec should confirm trust boundaries:
- Can internal endpoints be called directly from places they should not?
- Do services re-validate tokens when required by architecture?
- Are user identity headers signed or otherwise protected from spoofing inside the mesh?
This is advanced for pure black-box QA, but asking the question during design reviews prevents ugly production findings.
Negative Claim Matrix
Build a matrix of claim manipulations:
| Claim changed | New value | Expected |
|---|---|---|
| exp | past time | reject |
| exp | far future without valid resign | reject |
| sub | other user id without resign | reject |
| role | admin without resign | reject |
| aud | wrong API | reject |
| iss | attacker issuer URL | reject |
| tenant_id | other tenant without resign | reject |
Every row assumes signature validation still must fail closed when the token is not resignable by the client.
JWT Header Puzzles Worth a Look
Besides alg, inspect:
kidvalues: does the server fetch keys unsafely based on attacker-controlled kid?- Unexpected
typvalues. - Extremely large headers that may stress parsers.
If you observe strange key loading behavior, stop and involve security specialists. Some kid issues become SSRF-class problems.
Performance and Availability Side Notes
Security testing can accidentally become load testing.
Be careful with:
- Tight loops on login endpoints
- Massive concurrent refresh storms
- Huge malformed tokens that stress parsers
Stay inside agreed rates. Security quality includes being a good steward of shared staging.
Documentation QA Should Demand
Ask engineering for a short auth contract:
Access token TTL:
Refresh token TTL:
Rotation policy:
Revocation policy on logout:
Revocation policy on password change:
Required claims:
Accepted algorithms:
Clock skew tolerance:
Where public keys are published:
Without this, testers guess and severity debates drag on.
Mapping Findings to Fix Owners
| Finding type | Likely owner |
|---|---|
| Signature not verified | Auth service / API platform |
| Missing exp check | Auth library configuration |
| Role claim trusted without DB/policy check | App authorization layer |
| Refresh not revoked | Identity service |
| Token logged in errors | Observability + app handlers |
| Token in query string | Client teams |
Good routing shortens mean time to remediate.
Practice Script for New QA Hires
Day one JWT lab:
- Decode a staging token offline.
- Call a protected route with and without it.
- Flip one payload character and observe failure.
- Capture refresh flow.
- Write one defect-style note even if everything passes, describing residual risk and what was evidenced.
This builds confidence without requiring crypto expertise.
When you know how to test JWT authentication, you stop treating bearer tokens as magic strings and start treating them as verifiable trust objects with a lifecycle.
Conclusion
Learning how to test JWT authentication means proving that tokens are hard to forge, short-lived enough to manage risk, validated on every protected call, and paired with real authorization checks. Decode claims to understand them, then attack your own staging tokens with missing values, expiry, tampering, refresh reuse, and role edits.
When those tests are part of ordinary API regression, login is no longer a green checkbox. It becomes a verified trust boundary. That is what modern API quality requires.
FAQ
Questions testers ask
How do you test JWT authentication as a QA?
Obtain valid tokens from the real login or token endpoint, decode claims without trusting them blindly, then replay API calls with missing, expired, malformed, unsigned, and tampered tokens. Verify protected routes reject bad tokens, accept good ones, and honor role and subject claims server-side.
What claims should QA validate in a JWT?
Common claims include iss, aud, exp, nbf, iat, sub, and app-specific roles or tenant IDs. Testing means confirming the server enforces them, not only that they appear in a decoded payload. Expired tokens and wrong audience values should fail.
What is the JWT alg=none test?
It checks whether the API accepts a token that declares no signature algorithm and carries an empty or absent signature. Secure systems reject this. Acceptance can allow attackers to forge tokens with arbitrary claims.
How should refresh tokens be tested?
Verify refresh issues new access tokens, rejects reused revoked refresh tokens if rotation is required, binds to the right user, expires correctly, and does not expose refresh tokens to insecure storage patterns in clients you control. Test logout and password change revocation paths.
Is decoding a JWT enough to test security?
No. Anyone can decode a base64 payload. Security comes from server-side signature verification and claim enforcement. QA must send modified tokens to the API and observe accept or reject behavior.
Where should JWTs be stored in web apps?
Storage is an architecture choice with trade-offs. HttpOnly secure cookies reduce XSS token theft but need CSRF defenses. localStorage is easy for SPAs but readable by XSS. QA should test the chosen design's failure modes rather than arguing absolutes without context.
RELATED GUIDES
Continue the route
API Authentication Testing: OAuth, JWT, and API Keys
Learn API authentication testing for OAuth 2.0, JWT expiry and refresh flows, API keys vs bearer tokens, and broken authentication test cases.
Authentication and Authorization Testing
Learn authentication and authorization testing: RBAC, IDOR, session management, vertical privilege escalation, and broken access control test cases.
API Security Testing Checklist
Use this API security testing checklist for auth, access control, injection, rate limits, headers, and transport checks QA can run before release.
Security Testing for QA Engineers: An Introduction
Learn security testing for QA engineers: what checks you can run, how it differs from functional testing, and a practical release checklist for web apps.