Secure Your Go API: Implementing User Authentication
Building secure web applications is paramount, and user authentication is a fundamental pillar of that security. This challenge requires you to implement a robust authentication system for a Go-based API, enabling users to register, log in, and protect certain API endpoints. This is a common and crucial task for any developer building real-world web services.
Problem Description
Your task is to create a basic Go web server that handles user authentication. This involves:
- User Registration: Allow new users to register with a username and password.
- User Login: Allow registered users to log in using their credentials.
- Secure Endpoint: Implement a protected endpoint that can only be accessed by authenticated users.
- Token-Based Authentication: Use JSON Web Tokens (JWT) for managing user sessions and authorizing access to protected resources.
Key Requirements:
- Password Hashing: Passwords must be securely hashed before storage. Use a strong hashing algorithm like bcrypt.
- JWT Generation and Validation: Upon successful login, generate a JWT containing user information (e.g., username). This token should be returned to the client. For protected endpoints, the server must validate the incoming JWT.
- In-Memory User Storage: For this challenge, you can store user credentials (username and hashed password) in memory. In a production environment, you would use a database.
- HTTP Methods:
POST /register: For user registration.POST /login: For user login.GET /protected: A protected endpoint.
Expected Behavior:
- Registration: A successful registration should store the user's username and hashed password. The API should respond with a success message.
- Login: A successful login should return a JWT. An unsuccessful login (invalid credentials) should return an appropriate error.
- Protected Endpoint:
- If a request to
/protectedincludes a valid JWT in theAuthorizationheader (e.g.,Authorization: Bearer <token>), the request should be processed, and the user should receive a success message indicating they are authenticated. - If the request lacks a valid JWT or the token is invalid/expired, the API should return an unauthorized error (e.g., HTTP 401).
- If a request to
Edge Cases:
- Attempting to register with an existing username.
- Attempting to log in with incorrect credentials.
- Accessing a protected endpoint without a token.
- Accessing a protected endpoint with an invalid or expired token.
Examples
Example 1: User Registration
Input:
POST /register
Content-Type: application/json
{
"username": "alice",
"password": "securepassword123"
}
Output:
HTTP/1.1 201 Created
Content-Type: application/json
{
"message": "User 'alice' registered successfully"
}
Explanation: The API receives a POST request to /register with a username and password. It hashes the password, stores the user's credentials (username and hashed password) in memory, and returns a success message.
Example 2: User Login (Successful)
Input:
POST /login
Content-Type: application/json
{
"username": "alice",
"password": "securepassword123"
}
Output:
HTTP/1.1 200 OK
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFsaWNlIiwibmFtZSI6ImFsaWNlIiwiZXhwIjoxNzE4MjYyMzk3fQ.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // Example JWT (actual will vary)
}
Explanation: The API receives a POST request to /login. It verifies the provided password against the stored hashed password for "alice". Upon successful verification, it generates a JWT containing the username and returns it.
Example 3: Accessing Protected Endpoint (Authenticated)
Input:
GET /protected
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFsaWNlIiwibmFtZSI6ImFsaWNlIiwiZXhwIjoxNzE4MjYyMzk3fQ.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // (Use token from previous login)
Output:
HTTP/1.1 200 OK
Content-Type: application/json
{
"message": "Welcome, alice! You are authenticated."
}
Explanation: The API receives a GET request to /protected with a valid JWT in the Authorization header. It validates the token, extracts the username, and returns a personalized welcome message.
Example 4: Accessing Protected Endpoint (Unauthorized)
Input:
GET /protected
Output:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"message": "Unauthorized: Missing or invalid token"
}
Explanation: The API receives a GET request to /protected without an Authorization header. It rejects the request and returns an unauthorized error.
Constraints
- Hashing Algorithm: Use
golang.org/x/crypto/bcryptfor password hashing. - JWT Library: You can use a popular Go JWT library like
github.com/golang-jwt/jwt/v4. - Token Expiration: JWTs should have a reasonable expiration time (e.g., 1 hour).
- Secret Key: A strong, secret key should be used for signing JWTs. This should be managed securely (e.g., via environment variables in production).
- Concurrency: Your in-memory storage should be safe for concurrent access.
Notes
- Consider using Go's standard
net/httppackage for handling HTTP requests and routing. - You'll need to parse JSON request bodies and construct JSON responses.
- Think about how to handle errors gracefully and return informative messages to the client.
- For JWTs, you'll need to define the claims that will be stored in the token.
- The secret key for JWT signing is critical for security. For this challenge, you can define it as a constant, but be aware of best practices for managing secrets in real applications.