Implementing JWT Authentication in Python
This challenge focuses on building a foundational JWT (JSON Web Token) authentication system in Python. JWT is a widely used standard for securely transmitting information between parties as a JSON object. Implementing this will give you practical experience in creating secure token-based authentication, a critical skill for modern web applications.
Problem Description
You are tasked with creating a Python application that can:
- Generate JWTs: Given a user identifier and some arbitrary payload, create a signed JWT.
- Verify JWTs: Given a received JWT, validate its signature and expiration.
- Extract Claims: If a JWT is valid, extract the original claims (payload) from it.
This system should be robust enough to handle basic authentication scenarios.
Key Requirements:
- JWT Generation:
- The JWT should contain at least a
user_idand anexp(expiration time) claim. - A secret key must be used to sign the JWT. This secret key should be configurable.
- The JWT should be encoded using a common algorithm like HS256 (HMAC using SHA-256).
- The JWT should contain at least a
- JWT Verification:
- The verification process must check the JWT's signature against the provided secret key.
- The verification process must also check the
expclaim to ensure the token has not expired. - The algorithm used for verification must match the algorithm used for signing.
- Claim Extraction:
- If verification is successful, the function should return the decoded payload (claims) of the JWT.
- Error Handling:
- Appropriate exceptions should be raised for invalid tokens (e.g., expired, invalid signature).
Expected Behavior:
- A function
create_jwt(user_id: str, secret_key: str, algorithm: str = "HS256", expires_delta_seconds: int = 3600) -> strthat takes user ID, secret key, algorithm, and expiration time in seconds, and returns a JWT string. - A function
verify_jwt(token: str, secret_key: str, algorithm: str = "HS256") -> dictthat takes a JWT string, secret key, and algorithm. It should return the decoded payload (as a dictionary) if valid, and raise an appropriate exception otherwise.
Edge Cases to Consider:
- Tokens that have expired.
- Tokens signed with an incorrect secret key.
- Tokens with tampered payloads (which will result in an invalid signature).
- Tokens generated with different algorithms.
Examples
Example 1: Token Generation and Successful Verification
# Assume current time is epoch_time_now
secret_key = "my_super_secret_key"
user_id = "user123"
expires_delta_seconds = 3600 # 1 hour
# Step 1: Generate the JWT
# The payload will be something like:
# {
# "user_id": "user123",
# "exp": epoch_time_now + 3600
# }
generated_token = create_jwt(user_id, secret_key, expires_delta_seconds=expires_delta_seconds)
# Step 2: Verify the JWT
# The verify_jwt function will check the signature and expiration.
decoded_payload = verify_jwt(generated_token, secret_key)
# Expected Output:
# The decoded_payload dictionary will contain the original claims.
# For instance:
# {
# "user_id": "user123",
# "exp": <some_future_timestamp>
# }
Example 2: Verification of an Expired Token
# Assume current time is epoch_time_now
secret_key = "my_super_secret_key"
user_id = "user456"
# Create a token that expired a long time ago
past_time = 1678886400 # An arbitrary past timestamp
expires_delta_seconds = -3600 # Make it expire in the past
# Step 1: Generate the JWT (will have an expired exp claim)
expired_token = create_jwt(user_id, secret_key, expires_delta_seconds=expires_delta_seconds)
# Step 2: Attempt to verify the expired JWT
# This should raise an exception (e.g., ExpiredSignatureError)
try:
verify_jwt(expired_token, secret_key)
except Exception as e: # Replace with specific exception type if needed
print(f"Verification failed: {e}")
# Expected Output:
# Verification failed: Token has expired
Example 3: Verification with an Invalid Secret Key
secret_key_correct = "correct_secret"
secret_key_wrong = "wrong_secret"
user_id = "user789"
# Step 1: Generate a JWT with the correct secret key
valid_token = create_jwt(user_id, secret_key_correct)
# Step 2: Attempt to verify the token with the wrong secret key
# This should raise an exception (e.g., InvalidSignatureError)
try:
verify_jwt(valid_token, secret_key_wrong)
except Exception as e: # Replace with specific exception type if needed
print(f"Verification failed: {e}")
# Expected Output:
# Verification failed: Invalid signature
Constraints
- You must use the
PyJWTlibrary for JWT operations. - The
secret_keywill be a string of at least 16 characters. - The
user_idwill be a non-empty string. - The
expires_delta_secondswill be an integer. - The
algorithmwill be one of the commonly supported JWT algorithms (e.g., "HS256").
Notes
- You'll need to install the
PyJWTlibrary:pip install PyJWT. - Consider how to handle time. The
datetimemodule in Python will be crucial for managing expiration times. - Think about the payload structure for your JWTs.
- The
PyJWTlibrary provides specific exceptions for different types of verification failures (e.g.,jwt.ExpiredSignatureError,jwt.InvalidSignatureError). It's good practice to catch and handle these specific exceptions.