JWT Fundamentals: Understanding JSON Web Tokens
What is a JWT?
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed.
JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA. Signed tokens can verify the integrity of the claims contained within them, while encrypted tokens hide those claims from other parties.
JWT Structure
A JWT consists of three parts separated by dots (.):
xxxxx.yyyyy.zzzzz
These parts are:
- Header - Contains metadata about the token
- Payload - Contains the claims or assertions
- Signature - Verifies the token hasn't been altered
Let's examine each part in detail:
Header
The header typically consists of two parts:
- The type of token, which is JWT
- The signing algorithm being used, such as HMAC SHA256 or RSA
Example:
{
"alg": "HS256",
"typ": "JWT"
}
This JSON is Base64Url encoded to form the first part of the JWT.
Payload
The payload contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims:
-
Registered claims: Predefined claims which are not mandatory but recommended
iss(issuer): who issued the tokensub(subject): who the token refers toaud(audience): who the token is intended forexp(expiration time): when the token expiresnbf(not before): when the token starts being validiat(issued at): when the token was issuedjti(JWT ID): unique identifier for the token
-
Public claims: Claims defined by those using JWTs
- Should be defined in the IANA JSON Web Token Registry or as a URI that contains a collision-resistant namespace
- Private claims: Custom claims created to share information between parties
Example:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"exp": 1516242622
}
This JSON is Base64Url encoded to form the second part of the JWT.
Security Note: The payload is only Base64Url encoded, not encrypted. Anyone can decode it to view its contents. Never put sensitive information like passwords in the JWT payload unless you're using JWE (JSON Web Encryption).
Signature
The signature is created by taking the encoded header, the encoded payload, a secret, and the algorithm specified in the header, and signing them.
For example, if you're using the HMAC SHA256 algorithm:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
The signature is used to verify that the message wasn't changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.
JWT Encoding Process
- Create a header JSON object
- Create a payload JSON object
- Base64Url encode the header
- Base64Url encode the payload
- Create a signature using the encoded header, encoded payload, and a secret
- Concatenate the encoded header, encoded payload, and signature with dots
Header
{
"alg": "HS256",
"typ": "JWT"
}
Base64Url Encoded Header
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Base64Url Encoded Payload
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
Complete JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT Verification Process
- Split the JWT into its three parts
- Base64Url decode the header and payload
- Verify the signature using the algorithm specified in the header
- Check the claims (exp, nbf, iss, etc.) to ensure they're valid
- Process the JWT according to the application's requirements
JWT vs. Session Tokens
| Feature | JWT | Session Tokens |
|---|---|---|
| Storage | Client-side | Server-side |
| Scalability | Highly scalable (stateless) | Requires session storage |
| Security | Signed, can be encrypted | Opaque reference |
| Size | Larger (contains claims) | Smaller (just an ID) |
| Expiration | Self-contained | Controlled by server |
| Revocation | Difficult without a blacklist | Easy (delete from store) |
Common JWT Use Cases
Authentication
After a user logs in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token.
Information Exchange
JWTs are a good way of securely transmitting information between parties because they can be signed, which means you can be sure the senders are who they say they are.
Authorization
Once a user is logged in, an application can allow access to specific resources based on permissions encoded in the JWT.
Federated Identity
JWTs are used in single sign-on (SSO) scenarios where a user logs in once and gains access to multiple systems without being prompted to log in again.
JWT Best Practices
Security
- Use HTTPS: Always transmit JWTs over HTTPS to prevent token theft through network eavesdropping.
- Set Proper Expiration: Use short-lived tokens (minutes to hours, not days) to minimize the damage if a token is compromised.
- Validate All Claims: Always validate the issuer, audience, expiration, and other claims.
- Use Strong Secrets/Keys: For HMAC algorithms, use a strong, randomly generated secret. For RSA/ECDSA, use proper key lengths.
- Consider Using Refresh Tokens: Implement a refresh token system for obtaining new access tokens without requiring the user to re-authenticate.
Implementation
- Don't Store Sensitive Data: JWTs are not encrypted by default, so don't store sensitive information in the payload.
- Implement Token Revocation: Have a strategy for revoking tokens when needed (user logout, password change, etc.).
- Use Appropriate Algorithms: Prefer asymmetric algorithms (RS256, ES256) over symmetric ones (HS256) for production systems with multiple verification points.
- Validate Input: Always validate and sanitize user input before including it in a JWT.
- Handle Errors Securely: Don't expose detailed error messages that might help an attacker.
JWT Libraries
JavaScript/Node.js
Python
Ruby
Conclusion
JWTs provide a powerful, standardized way to handle authentication and authorization in modern applications. By understanding their structure, capabilities, and security considerations, you can implement them effectively and securely in your projects.
Remember that while JWTs offer many advantages, they're not a silver bullet for all authentication scenarios. Consider your specific requirements and security needs when deciding whether to use JWTs or alternative authentication mechanisms.