JWT is a compact way to send JSON data between two parties in a secure and tamper-proof manner.
The most common use case for JWTs is authorization.
For example, when you log into a website, the server gives you a JWT. After that, you send the JWT with each request to prove it’s you, without needing to enter your password again.
A JWT consists of three parts: header, payload, and signature. These parts are separated by periods and encoded in Base64URL format.
Specifies the token type and the algorithm used to sign it.
{
"typ": "JWT",
"alg": "HS256"
}
Carries claims, which can be any information you want to store, such as user details, roles, or other relevant data.
This information is protected against tampering, but is readable by anyone. Don’t put secret information in JWT unless it is encrypted.
{
"name": "Bojan Gabric",
"roles": ["ADMIN"]
}
You can also include some standard fields in the payload. These are optional, but if present, most JWT libraries will automatically check them when verifying the token:
Claim Name | Description |
---|---|
iss (Issuer) | Who issued the JWT (e.g. example.com) |
sub (Subject) | Who the JWT is about (usually the user ID) |
aud (Audience) | Who the JWT is meant for (like your API) |
exp (Expiration Time) | When the token should expire (in seconds since epoch) |
nbf (Not Before) | Token isn’t valid before this time |
iat (Issued At) | When the token was created (timestamp in seconds) |
jti (JWT ID) | A unique ID for this token |
Used to verify that the message hasn’t been altered during transmission, and in the case of private key signing, it can also confirm the identity of the sender.
It’s created by taking the header and payload, encoding them, and signing them with a secret key (for symmetric signing) or a private key (for asymmetric signing).
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
Once everything is encoded and signed with the secret, the final JWT will look like this:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiQm9qYW4gR2FicmljIiwicm9sZXMiOlsiQURNSU4iXX0.TvR-Ms-SQJ31UV1fIg6G6frtEKECx5d0TQ0Hs6xnr80
With symmetric signing, the same key is used for both signing and verifying the JWT. This method is commonly used in single-server applications or microservices where all services share a secret key to validate tokens.
Pros:
Cons:
With asymmetric signing, two different keys are used: a private key for signing and a public key for verification. This approach is useful when multiple services or third parties need to verify the token without accessing the private key.
Pros:
Cons:
If an attacker manages to steal a JWT, they can use it to:
Access user data
The stolen token allows the attacker to make authenticated requests to the
backend services. Since the server trusts the JWT, it will grant access to any
resources associated with the original user.
Perform actions on behalf of the user
Depending on the permissions associated with the token, the attacker may be
able to perform actions as the impersonated user, such as changing the account
settings, making transactions, or viewing sensitive information.
To minimize the risk of token theft, you can:
Use HTTPS
Always use HTTPS to encrypt data in transit. This prevents attackers from
intercepting JWTs as they move between the client and the server.
Secure Storage
To prevent access by JavaScript, avoid storing JWTs in places like
localStorage or sessionStorage and use HTTPOnly cookies instead.
Set Token Expiration
Keep JWT expiration times short. This reduces the time window for attackers to
use a stolen token. Implement token refreshing mechanisms for secure and
long-term sessions.
Rotate Keys Regularly
Regularly rotate your signing keys. This reduces the risk of key compromise
and ensures that stolen tokens won’t be valid for long periods.
Find this post helpful? Subscribe and get notified when I post something new!