JWT, or JSON Web Token, 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 are commonly used for authentication and authorization in web applications.
Structure of a JWT
A JWT typically consists of three parts, separated by dots ('.'), which are Base64Url encoded:
1. Header: Contains metadata about the token, typically the type of the token (JWT) and the signing algorithm being used (e.g., HMAC SHA256 or RSA). Example: `{"alg": "HS256", "typ": "JWT"}`.
2. Payload: Contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims:
- Registered claims: Pre-defined, non-mandatory claims like `iss` (issuer), `exp` (expiration time), `sub` (subject), `aud` (audience), `iat` (issued at time), etc.
- Public claims: Custom claims defined by parties using JWTs, but to avoid collisions, they should be defined in the IANA JSON Web Token Registry or be a URI that contains a collision-resistant name space.
- Private claims: Custom claims created to share information between parties that agree on their use, and they are neither registered nor public.
3. Signature: Created by taking the Base64Url encoded header, the Base64Url encoded payload, a secret key (or a private key for asymmetric algorithms), and the algorithm specified in the header. The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message hasn't been altered along the way.
How it Works (Authentication Flow)
1. A user logs in with their credentials (username/password).
2. The server verifies the credentials and, if valid, generates a JWT.
3. The JWT is sent back to the client.
4. The client stores the JWT (e.g., in `localStorage` or `sessionStorage`).
5. For subsequent requests to protected routes, the client includes the JWT, typically in the `Authorization` header as a `Bearer` token.
6. The server receives the request, extracts the JWT, and verifies its signature using the secret key (or public key).
7. If the signature is valid and the token hasn't expired, the server trusts the claims in the payload and grants access to the requested resource.
Advantages of JWTs
- Statelessness: The server doesn't need to store session information, making horizontal scaling easier.
- Compact: Due to their small size, JWTs can be sent through URL, POST parameter, or inside an HTTP header.
- Self-contained: They carry all the necessary user information directly within the token.
- Widely adopted: Many programming languages and frameworks have libraries for JWT creation and verification.
Disadvantages of JWTs
- Token Size: Can become large if too much information is put into the payload.
- Revocation: Revoking a JWT before its expiration can be complex, often requiring a blacklist mechanism.
- Security of Secret: The secret key must be kept absolutely confidential. If compromised, an attacker can forge tokens.
Example Code
import jwt
import datetime
import time
--- Configuration ---
A strong, secret key known only to the server.
In a real application, this should be loaded from environment variables or a secure configuration.
SECRET_KEY = "your-super-secret-key-that-no-one-will-guess"
ALGORITHM = "HS256" HMAC-SHA256
--- 1. Creating (Encoding) a JWT ---
print("--- Creating a JWT ---")
Define the payload (claims)
'exp' (expiration time) is a registered claim, essential for security.
'iat' (issued at time) is also a good practice.
payload = {
"user_id": 123,
"username": "johndoe",
"roles": ["admin", "user"],
"iat": datetime.datetime.utcnow(),
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1) Token expires in 1 hour
}
Encode the JWT
try:
token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
print(f"Generated JWT: {token}")
except Exception as e:
print(f"Error encoding token: {e}")
print("\n--- Decoding and Verifying a JWT ---")
--- 2. Decoding and Verifying a JWT ---
if 'token' in locals():
try:
Decode and verify the token
The library automatically checks 'exp' and 'iat' by default
decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
print(f"Decoded Payload: {decoded_payload}")
Access specific claims
print(f"User ID from token: {decoded_payload['user_id']}")
print(f"Username from token: {decoded_payload['username']}")
print(f"Roles from token: {decoded_payload['roles']}")
except jwt.ExpiredSignatureError:
print("Token has expired!")
except jwt.InvalidTokenError as e:
print(f"Invalid token: {e}")
except Exception as e:
print(f"An unexpected error occurred during decoding: {e}")
else:
print("No token was generated to decode.")
--- 3. Example of an Expired Token ---
print("\n--- Example of an Expired Token ---")
expired_payload = {
"user_id": 456,
"username": "janedoe",
"iat": datetime.datetime.utcnow() - datetime.timedelta(hours=2), Issued 2 hours ago
"exp": datetime.datetime.utcnow() - datetime.timedelta(hours=1) Expired 1 hour ago
}
expired_token = jwt.encode(expired_payload, SECRET_KEY, algorithm=ALGORITHM)
print(f"Generated Expired JWT: {expired_token}")
try:
jwt.decode(expired_token, SECRET_KEY, algorithms=[ALGORITHM])
print("Decoded expired token successfully (This should not happen if expiration is checked).")
except jwt.ExpiredSignatureError:
print("Successfully caught ExpiredSignatureError for the expired token.")
except jwt.InvalidTokenError as e:
print(f"Caught other InvalidTokenError: {e}")








JWT (JSON Web Tokens)