Secure Authentication with JWT in Go
Implementing JSON Web Token (JWT) handling is a fundamental skill for building secure and stateless authentication systems in web applications. This challenge will guide you through creating robust JWT generation and verification mechanisms in Go, enabling secure communication between clients and servers.
Problem Description
Your task is to develop a Go program that can both create (sign) and validate (verify) JSON Web Tokens. This will involve defining a structure for your token's claims, signing the token with a secret key, and then verifying a received token against that same secret key and ensuring its integrity and validity. This functionality is crucial for securely transmitting information between parties without needing to re-validate on each request.
Key Requirements:
- Token Generation: Create a function that takes a secret key and a set of custom claims (e.g., user ID, roles) and returns a signed JWT string.
- Token Verification: Create a function that takes a secret key and a JWT string, and returns the verified claims or an error if the token is invalid, expired, or has been tampered with.
- Error Handling: Implement proper error handling for invalid tokens, expired tokens, and signing/verification failures.
- Claim Structure: Define a clear Go struct to represent the claims within the JWT.
Expected Behavior:
- A valid token generated with a specific secret key should be successfully verified by a corresponding verification function using the same secret key.
- A token generated with one secret key should fail verification with a different secret key.
- An expired token should be rejected during verification.
- A token that has been tampered with (e.g., header or payload modified) should be rejected during verification.
Edge Cases to Consider:
- Empty or invalid secret keys.
- Tokens with malformed structures.
- Tokens with expired expiration times.
Examples
Example 1: Token Generation and Successful Verification
Input:
- Secret Key:
"mySuperSecretKey" - Claims:
{"userID": "123", "role": "admin", "exp": 1735689600}(Unix timestamp for Jan 1, 2025 00:00:00 UTC)
Output:
A valid JWT string (e.g., eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJ1c2VySUQiOiIxMjMifQ.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) and upon verification, the original claims should be returned.
Explanation: The program should sign the provided claims with the secret key to generate a JWT. When this generated JWT is then passed to the verification function with the same secret key, it should be successfully parsed, its signature validated, and the claims extracted.
Example 2: Verification Failure (Invalid Signature)
Input:
- Secret Key:
"mySuperSecretKey" - JWT String: (A JWT generated with a different secret key, e.g.,
"anotherSecret")
Output: An error indicating an invalid signature.
Explanation: The verification function attempts to verify the token's signature using the provided secret key. Since the secret key used for signing was different, the signature will not match, and the verification will fail.
Example 3: Verification Failure (Expired Token)
Input:
- Secret Key:
"mySuperSecretKey" - JWT String: (A JWT generated with
{"userID": "456", "exp": 1672531200}) (Unix timestamp for Jan 1, 2023 00:00:00 UTC) - Current Time (for verification): Assume current time is significantly after Jan 1, 2023.
Output: An error indicating that the token has expired.
Explanation:
The verification function checks the exp (expiration time) claim within the token's payload. If the current time is past the expiration time specified in the token, verification fails.
Constraints
- The JWT signing algorithm should be HS256 (HMAC using SHA-256).
- The secret key will be a string.
- Claims will be represented by a Go struct.
- The program should handle tokens with an
expclaim (expiration time) as a Unix timestamp. - Performance should be reasonable for typical web request scenarios; complex optimization is not the primary focus.
Notes
- Consider using an established JWT library for Go to handle the complexities of JWT parsing and signing. This will allow you to focus on the logic of claim management and integration.
- Pay close attention to the data types when handling claims, especially the expiration time which is typically a Unix timestamp.
- When defining your custom claims struct, ensure it embeds or includes standard JWT claims like
expfor proper validation.