JWT (JSON Web Token)
Introduction
JWT, or JSON Web Token, is one of the most common token formats used in modern web applications and REST APIs. Applications need a secure and efficient way to transmit identity and authorization information between systems. A user may log in once, receive a token, and then call several protected APIs. A microservice may receive a request and need to know which user made the request, which roles the user has, which tenant the user belongs to, and whether the token is still valid. JWT provides a compact way to carry that information as a digitally signed token.
A JWT is commonly used with OAuth 2.0, OpenID Connect, bearer token authentication, REST APIs, microservices, mobile applications, and single-page applications. After successful authentication, an authorization server or authentication service may issue a JWT. The client sends that JWT in the `Authorization` header using the bearer token scheme. The API validates the JWT before trusting any information inside it. If validation succeeds, the API can use the claims inside the token to make authentication and authorization decisions.
JWT is popular because it supports stateless authentication. In traditional session-based authentication, the server often stores session data and the client sends a session ID. With JWT-based authentication, the token can carry the required claims, and the API can validate the token without looking up a server-side session for every request. This makes JWT useful in distributed systems where many services need to validate requests independently. It also makes token-based API testing a critical skill for QA engineers and SDETs.
However, JWT is often misunderstood. A standard signed JWT is not encrypted. Its header and payload are Base64URL-encoded, which means anyone who has the token can decode and read them. The signature protects integrity; it proves that the token was issued by a trusted party and has not been modified. It does not hide the payload. Testers must understand this distinction because storing sensitive information in a JWT payload is a serious mistake. A secure API must validate the signature, expiration, issuer, audience, scopes, roles, and other claims before granting access.
What Is JWT?
JWT stands for JSON Web Token. It is an open standard, defined by RFC 7519, for securely transmitting claims between two parties as a compact JSON-based token. A claim is a piece of information about a subject, such as user ID, username, role, issuer, audience, token expiry, or permission. The token is compact enough to be sent in HTTP headers and URL-safe enough to be transported through web protocols.
A simple definition is this: a JWT is a digitally signed token that carries user or client information and is commonly used for authentication and authorization. It allows a server to issue a token after login and allows APIs to validate that token later. Because the token is signed, the API can detect if the payload has been changed. If an attacker modifies the role from `Employee` to `Admin`, signature verification should fail.
JWT is a token format, not a complete authentication framework by itself. OAuth 2.0 is an authorization framework that may use JWT as the access token format. OpenID Connect may use JWT as the ID token format. Bearer Token Authentication is an HTTP authentication scheme that can carry a JWT or an opaque token. This distinction matters in interviews and testing. A JWT tells you how token data is structured. OAuth tells you how tokens are obtained and authorized. Bearer tells you how a token is presented to the API.
Why JWT Is Used
JWT is used because it is compact, self-contained, digitally signed, and convenient for distributed API systems. A JWT can carry enough information for an API to identify the subject, validate expiry, check issuer and audience, and apply authorization rules. This reduces the need for every API call to query a central session store, although some systems still perform additional lookup or introspection for revocation and policy checks.
JWT also works well across platforms. A token issued by an identity provider can be validated by services written in Java, JavaScript, .NET, Python, Go, or other technologies, as long as they share the trust configuration and signing keys. This makes JWT useful in microservices and enterprise systems where different services may be built with different stacks.
Another reason is stateless authentication. In a stateless model, the server does not need to store session data for every user request. The token carries claims, and the service validates it. This can improve scalability when designed correctly. It also fits REST principles because each request can carry the authentication information needed for processing. Testers should still remember that stateless does not mean careless. Tokens must be validated every time.
JWT Authentication Flow
A typical JWT flow starts when the user logs in. The user submits credentials to an authentication server. The server validates the credentials. If the credentials are correct, the server creates a JWT with claims such as subject, role, issuer, audience, issued-at time, and expiration. The server signs the token and returns it to the client. The client stores the token according to the application's security design and sends it with protected API requests.
POST /login
Content-Type: application/json
{
"username": "admin",
"password": "password123"
}
The server may return a token response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}
The client then calls a protected API:
GET /employees
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
The API validates the JWT before processing the request. It decodes the header and payload, verifies the signature, checks expiration, validates issuer and audience, and checks required claims or scopes. If the token is valid and authorized, the API returns the response. If the token is invalid, expired, tampered, or insufficient, the API rejects the request.
JWT Structure
A JWT consists of three parts separated by dots. The structure is:
Header.Payload.Signature
A token may look like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjMiLCJuYW1lIjoiSm9obiIsInJvbGUiOiJBZG1pbiJ9.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
The first part is the header. The second part is the payload. The third part is the signature. Header and payload are Base64URL-encoded JSON. The signature is generated from the encoded header, encoded payload, and a signing key. This three-part structure makes JWT easy to transport and easy to validate with standard libraries.
| JWT Part | Purpose |
|---|---|
| Header | Describes token type and signing algorithm |
| Payload | Contains claims about the user, client, token, or permissions |
| Signature | Verifies the token has not been modified and was signed by a trusted key |
JWT Header
The JWT header contains metadata about the token. It usually identifies the token type and the signing algorithm. A simple header may look like this:
{
"alg": "HS256",
"typ": "JWT"
}
The `alg` field tells which signing algorithm is used. `HS256` means HMAC using SHA-256 with a shared secret. Other systems may use asymmetric algorithms such as `RS256`, where a private key signs the token and a public key verifies it. The `typ` field commonly says `JWT`, though validation should not rely only on that value.
Testers do not usually create production JWT headers manually, but they should understand what they mean. If the algorithm is weak, unsupported, unexpected, or changed by an attacker, the API should reject the token. A classic JWT security mistake is accepting a token with an unsafe algorithm or failing to enforce the expected signing method. Modern libraries help prevent this when configured correctly.
JWT Payload
The JWT payload contains claims. Claims are pieces of information that the issuer states about the subject, token, or authorization context. A simple payload may look like this:
{
"sub": "101",
"name": "John",
"role": "Admin"
}
The payload is Base64URL-encoded, not encrypted by default. Anyone who has the token can decode the payload and read these claims. For that reason, sensitive secrets should not be placed in a normal signed JWT payload. Do not store passwords, full credit card numbers, private keys, sensitive personal data, or confidential business secrets in a readable JWT. If confidentiality is required, a different design or encrypted token approach is needed.
The API should not blindly trust payload claims just because they are readable. It must verify the signature first. If a tester decodes a JWT and changes the role from `Employee` to `Admin`, the token payload may look convincing, but the signature should no longer match. The API must reject the modified token. This is a core JWT testing scenario.
Types of JWT Claims
JWT claims are commonly grouped into registered claims, public claims, and private claims. Registered claims are standard names defined by the JWT specification. They are not mandatory in every token, but they provide common meanings. Important registered claims include `iss` for issuer, `sub` for subject, `aud` for audience, `exp` for expiration time, `nbf` for not before, `iat` for issued at, and `jti` for JWT ID.
The `iss` claim identifies who issued the token. The `aud` claim identifies the intended audience, often the API or service that should accept the token. The `exp` claim defines when the token expires. The `nbf` claim defines when the token becomes valid. The `iat` claim indicates when it was issued. The `jti` claim can provide a unique token identifier, useful for replay detection or revocation strategies.
Public claims are custom claim names agreed upon by applications, often designed to avoid naming collisions. Private claims are application-specific claims shared between trusted systems. Examples include `department`, `employeeId`, `tenantId`, `roles`, or `permissions`. These claims can be useful, but they should be validated carefully and should not include sensitive data that should remain confidential.
JWT Signature
The signature protects token integrity. It is generated using the encoded header, encoded payload, and a signing key. A simplified HMAC-style signature process looks like this:
HMACSHA256(
Base64Url(Header) + "." + Base64Url(Payload),
SecretKey
)
When the API receives the JWT, it recalculates or verifies the signature using the expected key and algorithm. If the header or payload has been changed, the signature verification fails. This prevents attackers from modifying claims and still having the API trust the token. For example, changing the subject, role, expiry, or scope should invalidate the signature.
There are two broad signing approaches. Symmetric signing uses the same secret key to sign and verify the token. Asymmetric signing uses a private key to sign and a public key to verify. Asymmetric signing is common in distributed systems because APIs can validate tokens using public keys without having access to the private signing key. Testers should know which model the system uses when debugging token validation issues.
JWT Validation
JWT validation is the process of deciding whether a token should be trusted. A secure API does not merely decode the token and read claims. It must validate the signature, check the algorithm, verify expiry, confirm issuer, confirm audience, evaluate not-before time, and enforce required scopes, roles, or permissions. If any required validation fails, the API should reject the request.
A strong validation flow looks like this: receive the JWT, parse the structure, decode header and payload, verify signature with the expected key, validate signing algorithm, check `exp`, check `nbf`, check `iss`, check `aud`, validate scopes or roles, then process the request if authorization allows it. The order may differ internally, but all required checks must be present.
For API testers, JWT validation provides many negative test cases. A missing token should fail. A token with invalid structure should fail. A token with a modified payload should fail. A token with an invalid signature should fail. An expired token should fail. A token issued by the wrong issuer should fail. A token intended for another audience should fail. A valid token with insufficient role should be forbidden.
JWT Expiration
JWTs commonly include the `exp` claim, which represents expiration time. The value is usually a Unix timestamp. The API checks whether the current time is before the expiration time. If the token has expired, the API should reject it. A common response is `401 Unauthorized` because the token is no longer valid for authentication.
{
"exp": 1752000000
}
Expiration is important because JWTs can be difficult to revoke once issued, especially if they are validated statelessly. Short-lived JWTs reduce risk. If a token leaks, it can be used only until expiration. Long-lived JWTs increase exposure. Many systems pair short-lived access tokens with refresh tokens so users can remain signed in without making access tokens valid for too long.
Testing expiration should be deliberate. A tester should verify that expired JWTs fail and valid unexpired JWTs succeed. If the test environment allows generated tokens, create one with an expired `exp` claim and a valid signature. If not, use test support from the identity provider or wait for a short-lived token to expire. Do not merely decode the token and assume the API checks expiry. Validate the actual API response.
JWT vs Session Authentication
JWT-based authentication is often compared with session authentication. In session authentication, the server creates a session after login and stores session state. The client receives a session ID, often in a cookie. On each request, the server looks up the session ID to find the user and session data. This is common in traditional web applications.
With JWT authentication, the token can carry claims and be validated without a server-side session lookup. This makes JWT stateless from the resource server's perspective. Stateless validation can scale well in REST APIs and microservices because multiple services can validate the token independently. However, stateless validation also makes immediate revocation harder unless additional mechanisms are added.
| Point | JWT | Session Authentication |
|---|---|---|
| State model | Often stateless | Stateful |
| Server storage | No session storage required for basic validation | Server stores session data |
| API fit | Strong fit for REST APIs and microservices | Common in traditional web apps |
| Revocation | May require extra strategy | Can invalidate server-side session |
| Payload | Can carry claims | Session data stored server-side |
Neither approach is automatically correct for every system. JWT is useful for distributed APIs, while sessions can be excellent for server-rendered web applications. Testers should understand the chosen model and validate its security properties accordingly.
JWT vs OAuth 2.0
JWT and OAuth 2.0 are frequently confused. JWT is a token format. OAuth 2.0 is an authorization framework. OAuth defines roles, grant types, token issuance flows, scopes, and access delegation. JWT defines a compact way to represent claims in a signed token. OAuth may use JWT as the access token format, but it can also use opaque tokens.
| Point | JWT | OAuth 2.0 |
|---|---|---|
| Category | Token format | Authorization framework |
| Main purpose | Transmit claims in a signed token | Define how clients obtain authorized access |
| Contains claims | Yes | May issue tokens with or without readable claims |
| Grant types | No | Yes |
| Can work together | Can be used as OAuth access token or ID token | Can issue JWT or opaque tokens |
A strong interview answer should say that JWT is not OAuth. OAuth may use JWT, and OpenID Connect often uses JWT for ID tokens, but they are different concepts. This distinction helps testers understand whether they are testing token format, token issuance, or token enforcement.
JWT vs Bearer Token
JWT and bearer token are also different concepts. A bearer token is an authentication scheme where whoever possesses the token can present it to access protected resources. A JWT is one possible token format. A bearer token may be a JWT or an opaque string. When an API says it expects `Authorization: Bearer`, it is describing how the token is sent, not necessarily what format the token uses.
| Point | JWT | Bearer Token |
|---|---|---|
| Category | Specific token format | HTTP authentication scheme |
| Structure | Header.Payload.Signature | Any token sent as bearer credential |
| Claims | Often self-contained | May be JWT or opaque |
| Usage | Can be sent as a bearer token | Defines token presentation in Authorization header |
For testing, this means the tester should know both the transport and the format. The request may use the bearer header, and the token inside may be a JWT. The API must validate both that the token is presented correctly and that the JWT is trustworthy.
JWT in API Testing
JWT testing should cover valid and invalid token behavior. A valid JWT should allow access only when it is signed by a trusted issuer, unexpired, intended for the correct audience, and authorized for the requested operation. A missing JWT should fail. A malformed JWT should fail. A JWT with an invalid signature should fail. A JWT with a modified payload should fail. A JWT with an expired `exp` claim should fail. A JWT with a wrong issuer or audience should fail.
Role and permission claims must also be tested. If the token says the user is an employee, the API should not allow admin actions. If the token has read scope only, the API should not allow write operations. If the token belongs to one tenant, it should not access another tenant's data. JWT validation and authorization must work together.
Testers should avoid creating unsafe shortcuts in automation. Do not use unsigned tokens unless the system explicitly supports them for a controlled test mode. Do not disable signature validation in test environments and then assume production is safe. Do not use long-lived tokens everywhere because that hides expiration behavior. A good test suite includes token lifecycle and negative security checks.
Example Test Cases
| Scenario | Expected Result | Purpose |
|---|---|---|
| Valid JWT with required claims | 200 OK or expected success status | Confirms authenticated access works |
| Missing JWT | 401 Unauthorized | Confirms protected endpoint rejects anonymous calls |
| Expired JWT | 401 Unauthorized | Confirms `exp` is enforced |
| Modified payload | 401 Unauthorized | Confirms signature validation catches tampering |
| Invalid signature | 401 Unauthorized | Confirms untrusted tokens are rejected |
| Wrong issuer | 401 Unauthorized or documented error | Confirms issuer validation |
| Wrong audience | 401 Unauthorized or documented error | Confirms audience validation |
| Insufficient role or scope | 403 Forbidden | Confirms authorization enforcement |
These test cases should use controlled tokens and clear expected outcomes. If a test is intended to validate authorization, the JWT should be valid but insufficient. If a test is intended to validate authentication, the JWT should be missing, invalid, expired, or tampered. Mixing these categories makes failures harder to understand.
REST Assured Example
REST Assured can send a JWT as a bearer token in the authorization header:
given()
.header("Authorization", "Bearer " + jwtToken)
.when()
.get("/employees")
.then()
.statusCode(200);
A missing-token test omits the header:
given()
.when()
.get("/employees")
.then()
.statusCode(401);
An authorization failure can use a valid JWT with a lower role:
given()
.header("Authorization", "Bearer " + employeeJwt)
.when()
.delete("/employees/101")
.then()
.statusCode(403);
In real automation, JWTs should be generated through a supported login or token endpoint when possible. If test tokens are manually generated, the test framework must use secure test keys and avoid leaking token values in logs. Request and response logging should redact `Authorization` headers.
Postman Example
In Postman, a tester can open the Authorization tab, select Bearer Token, and paste the JWT. Postman automatically sends the header in the required format:
Authorization: Bearer <jwt>
Postman is also useful for decoding and inspecting JWTs, but inspection is not the same as validation. A decoded JWT may show claims, but the API still needs to verify the signature and trust rules. Testers should send valid and tampered tokens to the API and confirm the actual response.
Postman environments can store tokens, but care is needed when exporting or sharing collections. JWTs may contain user information and may grant access while valid. Tokens should not be exposed in shared screenshots, public workspaces, or exported environment files.
Karate Example
Karate can send a JWT as a bearer token with a simple header step:
Given header Authorization = 'Bearer ' + jwt
When method GET
Then status 200
Karate can also call a login endpoint, extract a JWT from the response, and reuse it in later requests. This is useful when tokens expire or when different roles need different tokens. A framework may create admin, manager, employee, and read-only JWTs for different scenarios.
Negative JWT testing in Karate should be explicit. A scenario for expired token should use an expired token and expect `401`. A scenario for insufficient role should use a valid low-privilege token and expect `403`. This keeps authentication and authorization tests cleanly separated.
Real-World Examples
Google and other identity providers may use JWTs in OAuth and OpenID Connect scenarios, especially for ID tokens or service account flows. Microsoft Entra ID, formerly Azure AD, commonly issues JWT access tokens and ID tokens. These tokens contain claims such as issuer, audience, subject, tenant, expiry, roles, and scopes. APIs validate those claims before granting access.
Spring Boot applications often use Spring Security with JWT for stateless REST API authentication. After login, the application issues or accepts a JWT. Each protected endpoint validates the token before processing. Microservice platforms may use JWTs so services can pass user identity and authorization context through a distributed request chain.
Single-page applications and mobile applications often receive tokens from an identity provider and use them to call backend APIs. The frontend should handle tokens according to security guidance, while backend APIs must validate every token. Testers should avoid assuming that frontend login alone protects the backend. Direct API calls with missing or tampered JWTs must fail.
Advantages of JWT
JWTs support stateless authentication, compact transmission, digital signatures, cross-platform compatibility, custom claims, and scalable distributed validation. They are easy to send in HTTP headers and widely supported by libraries and identity providers. Because claims can be carried inside the token, APIs can often make access decisions without a session lookup for every request.
JWTs also fit microservices well. A gateway or identity provider can issue a token, and multiple services can validate it using shared trust configuration. Asymmetric signing allows services to validate tokens with public keys while the private signing key remains protected by the issuer. This is useful in large systems where many APIs need to trust one identity provider.
For testers, JWTs make some validations visible. The tester can decode a token and inspect claims such as expiry, issuer, audience, scope, or role. This helps debug failures. However, decoding is only a diagnostic tool. The API response remains the source of truth for whether validation is implemented correctly.
Limitations of JWT
A standard signed JWT is readable. This is one of the most important limitations. If sensitive data is placed in the payload, anyone who obtains the token can decode it. The signature prevents modification, not reading. If confidentiality is required, the design should avoid sensitive claims or use encryption such as JWE where appropriate.
Revocation can also be challenging. If an API validates JWTs statelessly and does not check a revocation list or session state, a token may remain valid until expiration even after logout or account changes. Short-lived tokens reduce this risk, and additional mechanisms can support revocation, but the design must be intentional.
JWT size can become a problem if too many claims are added. Large tokens increase request header size and network overhead. They may also expose more information than necessary. Keep JWT claims minimal. Include what the API needs, not every detail about the user.
Signing keys must be protected. If a shared secret or private key is leaked, attackers may create valid tokens. Key rotation and secure key management are important. Testers may not manage keys directly, but they should understand the risk and validate behavior during key rotation if it affects the system.
Best Practices
Always use HTTPS when transmitting JWTs. Even though the token is signed, it can still be stolen if sent over an insecure channel. Use short-lived JWTs, especially for access tokens. Validate the signature before trusting any claim. Validate `exp`, `iss`, `aud`, and other required claims. Reject tokens with unexpected algorithms or missing required fields. Follow least privilege when adding roles and scopes.
Do not store sensitive information in the JWT payload unless the token is encrypted and the design explicitly supports that. Avoid logging JWTs. Redact authorization headers in API logs, gateway logs, automation logs, CI logs, screenshots, and reports. Store tokens securely according to client type. Use refresh tokens or re-authentication for long-lived sessions rather than long-lived access JWTs.
Rotate signing keys when appropriate and support key rollover safely. If the identity provider exposes public keys through a JWKS endpoint, APIs should handle key updates correctly. Testers should validate that old and new keys work during rollover according to the design and that tokens signed with unknown keys fail.
Common Mistakes
A common mistake is assuming JWT is encrypted. A normal signed JWT is encoded and signed, not encrypted. Anyone can decode the header and payload. Never place passwords or sensitive secrets in readable claims. If a token appears in browser storage, logs, or reports, its payload may expose user information even if the token is expired.
Another serious mistake is skipping signature validation. Decoding a JWT and reading the role is not enough. The API must verify the signature before trusting claims. Otherwise, an attacker could create or modify a token and grant themselves admin privileges. Testers should tamper with payload claims and confirm the API rejects the token.
Ignoring expiration is also common. If the API accepts expired JWTs, token lifetime becomes meaningless. Long-lived JWTs should be avoided for access tokens unless there is a strong reason and compensating controls. Testers should include expiry checks in automation.
Logging JWTs is another common problem. A JWT may grant access while valid and may reveal user details when decoded. Full tokens should be masked in logs and reports. Hardcoding JWTs in test code is also risky because tokens expire, leak, and become unreliable test data.
Interview Questions
A common interview question is: what is JWT? A strong answer is that JWT, or JSON Web Token, is a compact, URL-safe, digitally signed token format used to transmit claims between parties. It is commonly used in REST APIs, OAuth 2.0, OpenID Connect, microservices, and stateless authentication.
Another question is: what are the three parts of a JWT? The answer is header, payload, and signature. The header describes the token type and algorithm. The payload contains claims. The signature verifies integrity and authenticity.
Interviewers often ask whether JWT is encrypted. The answer is not by default. A standard signed JWT, also known as JWS, is Base64URL-encoded and digitally signed, but its contents are readable. Encryption requires JWE. This is why sensitive data should not be stored in the payload of a normal JWT.
They may also ask what testers should validate. A strong answer includes valid JWTs, missing JWTs, malformed JWTs, expired JWTs, tampered payloads, invalid signatures, wrong issuer, wrong audience, invalid algorithm, role and scope claims, authorization checks, proper `401` and `403` responses, HTTPS usage, and secure token handling in logs and reports.
Interview-Ready Explanation
JWT, or JSON Web Token, is an open standard used to transmit claims between parties in a compact and URL-safe format. A JWT has three parts: header, payload, and signature. The header defines metadata such as token type and signing algorithm. The payload contains claims such as user ID, roles, issuer, audience, and expiration. The signature verifies that the token has not been modified and was signed by a trusted authority.
After a user or client authenticates, the server may issue a JWT. The client sends the JWT in the `Authorization: Bearer` header when calling protected APIs. The API validates the signature, expiration, issuer, audience, and required claims before granting access. JWT is widely used in REST APIs, OAuth 2.0, OpenID Connect, microservices, mobile applications, and single-page applications because it supports stateless authentication and distributed validation.
A standard signed JWT is not encrypted. Its payload is readable after Base64URL decoding, so sensitive information should not be placed inside unless encryption is used. During API testing, testers should validate valid tokens, missing tokens, expired tokens, tampered payloads, invalid signatures, wrong issuer, wrong audience, role-based authorization, scope enforcement, secure transmission over HTTPS, and token masking in logs.
Key Takeaway
JWT is a compact signed token format used to carry claims between systems. It has three parts: header, payload, and signature. The signature protects integrity, but a standard JWT payload is readable. JWT is useful for stateless REST APIs, OAuth 2.0, OpenID Connect, microservices, and distributed systems, but it must be validated correctly.
For API testers, the practical rule is simple: never trust a JWT just because it can be decoded. Validate actual API behavior. Check signature validation, expiration, issuer, audience, scopes, roles, missing tokens, malformed tokens, tampered tokens, and authorization boundaries. A secure JWT implementation depends on correct signing, careful claim design, short token lifetimes, protected keys, HTTPS, and safe handling of token values.