How to Validate JWT Claims (exp, iss, aud) in Python
This code demonstrates how to decode and validate a JWT's essential claims—expiration (exp), issuer (iss), and audience (aud)—using the PyJWT library, returning clear error messages for common validation failures.
pip install PyJWT
Python code
42 linesimport jwt
from datetime import datetime, timezone, timedelta
SECRET = "mock-secret"
def validate_token(token, expected_iss, expected_aud):
try:
decoded = jwt.decode(
token,
SECRET,
algorithms=["HS256"],
options={"require": ["exp", "iss", "aud"]},
audience=expected_aud,
issuer=expected_iss
)
return {"valid": True, "claims": decoded}
except jwt.ExpiredSignatureError:
return {"valid": False, "error": "Token expired"}
except jwt.InvalidAudienceError:
return {"valid": False, "error": "Audience mismatch"}
except jwt.InvalidIssuerError:
return {"valid": False, "error": "Issuer mismatch"}
except jwt.InvalidTokenError as e:
return {"valid": False, "error": str(e)}
if __name__ == "__main__":
now = datetime.now(timezone.utc)
valid_token = jwt.encode(
{"exp": now + timedelta(hours=1), "iss": "auth-service", "aud": "my-api", "user": "alice"},
SECRET,
algorithm="HS256"
)
expired_token = jwt.encode(
{"exp": now - timedelta(minutes=5), "iss": "auth-service", "aud": "my-api", "user": "bob"},
SECRET,
algorithm="HS256"
)
print("Valid token:", validate_token(valid_token, "auth-service", "my-api"))
print("Expired token:", validate_token(expired_token, "auth-service", "my-api"))
print("Wrong issuer:", validate_token(valid_token, "wrong-issuer", "my-api"))
print("Wrong audience:", validate_token(valid_token, "auth-service", "other-api"))
Output
Valid token: {'valid': True, 'claims': {'exp': 1712345678, 'iss': 'auth-service', 'aud': 'my-api', 'user': 'alice'}}
Expired token: {'valid': False, 'error': 'Token expired'}
Wrong issuer: {'valid': False, 'error': 'Issuer mismatch'}
Wrong audience: {'valid': False, 'error': 'Audience mismatch'}
How it works
The jwt.decode() function is the core of this validation. By passing options={'require': ['exp', 'iss', 'aud']}, you ensure these claims are present in the token. The issuer and audience parameters let you enforce exact matches for the issuer and audience. PyJWT raises specific exceptions for each validation failure—ExpiredSignatureError for expired tokens, InvalidAudienceError for audience mismatches, and InvalidIssuerError for issuer mismatches—which the function catches to return a readable error. The datetime.now(timezone.utc) ensures timezone-aware timestamps, which PyJWT requires for correct expiration comparison.
Common mistakes
- Forgetting to install PyJWT with pip, leading to ModuleNotFoundError
- Using naive datetime (without timezone) which PyJWT rejects
- Not passing `options={'require': [...]}` so missing claims are ignored
- Catching `InvalidTokenError` before specific exceptions, hiding details
Variations
- Manually check claims by decoding without verification and comparing exp, iss, aud yourself
- Use `jwt.decode` with a custom leeway parameter to allow small clock skew
Real-world use cases
- Validating access tokens in a microservices API gateway before forwarding requests
- Checking the issuer and audience of JWTs in a single sign-on (SSO) system
- Ensuring tokens are not expired when processing them in a background job or batch process
Sponsored
More from API design & gRPC
- Build a Bulk Array POST Mock Server in Python medium
- Build a Mock REST API with PUT and GET in Python medium
- Convert Protobuf to JSON and Dict in Python easy
- Create a Data Helper in Python for gRPC-style APIs easy
- Format data in Python using dataclasses like gRPC messages easy
- Generate an OpenAPI Spec from Mock Routes in Python easy
Keep learning
Related tutorials and quizzes for this topic.