Secure Your API: JWT Authentication in Python
JWT (JSON Web Token) authentication is a standard method for securing APIs. This challenge asks you to implement a basic JWT authentication system in Python, allowing you to generate, verify, and decode JWTs. This is crucial for building secure and scalable applications where you need to authenticate users and authorize access to resources.
Problem Description
You are tasked with creating a Python module that handles JWT authentication. The module should include functions for:
- Generating a JWT: This function should take a payload (a dictionary containing user information, e.g., user ID, username) and a secret key as input. It should generate a signed JWT using the provided payload and secret key. The JWT should be encoded as a string.
- Verifying a JWT: This function should take a JWT string and the secret key as input. It should verify the signature of the JWT and return the decoded payload if the JWT is valid. If the JWT is invalid (e.g., signature mismatch, expired), it should raise an exception.
- Decoding a JWT: This function should take a JWT string and the secret key as input. It should decode the JWT without verifying the signature. This is useful for scenarios where you only need to access the payload information without validating the token's authenticity. If the JWT is invalid, it should raise an exception.
Key Requirements:
- Use the
PyJWTlibrary for JWT generation and verification. You'll need to install it:pip install PyJWT - The secret key should be used for both signing and verifying JWTs.
- The payload should be a Python dictionary.
- The JWT should be encoded using UTF-8.
- The expiration time (exp) claim should be included in the payload when generating a JWT. Set the expiration time to 3600 seconds (1 hour) from the current time.
Expected Behavior:
- The
generate_jwtfunction should return a valid JWT string. - The
verify_jwtfunction should return the decoded payload if the JWT is valid. - The
decode_jwtfunction should return the decoded payload if the JWT is valid. - All three functions should raise an exception (e.g.,
InvalidTokenError) if the JWT is invalid or cannot be processed.
Edge Cases to Consider:
- Invalid JWT format (e.g., missing header, payload, or signature).
- Signature mismatch (JWT has been tampered with).
- Expired JWT (the
expclaim has passed). - Invalid secret key.
- Empty payload.
Examples
Example 1:
Input: payload = {"user_id": 123, "username": "testuser"}, secret_key = "my_secret_key"
Output: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInVzZXJuYW1lIjoicGVzdHVzZXIiLCJleHAiOjE3MDc0ODU0MDB9.some_signature" (The signature part will vary)
Explanation: A JWT is generated with the given payload and secret key, including an expiration time.
Example 2:
Input: jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInVzZXJuYW1lIjoicGVzdHVzZXIiLCJleHAiOjE3MDc0ODU0MDB9.some_signature", secret_key = "my_secret_key"
Output: {'user_id': 123, 'username': 'testuser', 'exp': 1707485400}
Explanation: The JWT is verified against the secret key, and the decoded payload is returned.
Example 3: (Expired JWT)
Input: jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInVzZXJuYW1lIjoicGVzdHVzZXIiLCJleHAiOjE2OTgwNzA0MDB9.some_signature", secret_key = "my_secret_key" (Assuming the current time is later than the expiration time in the JWT)
Output: Raises InvalidTokenError (or a similar exception indicating an invalid token)
Explanation: The JWT has expired, so verification fails and an exception is raised.
Constraints
- The secret key must be a string of at least 16 characters.
- The payload must be a dictionary.
- The expiration time (exp) claim must be a Unix timestamp (seconds since epoch).
- The JWT library (
PyJWT) must be used. - The code should be well-documented and easy to understand.
Notes
- Consider using the
datetimemodule to calculate the expiration time. - Handle potential exceptions gracefully and provide informative error messages.
- Focus on the core functionality of generating, verifying, and decoding JWTs. You don't need to implement user registration or authentication flows.
- The signature of the generated JWT will vary depending on the secret key and the order of keys in the payload dictionary. Don't hardcode the signature in your tests.
- Think about how to structure your code for reusability and maintainability. Consider creating a class to encapsulate the JWT authentication logic.