API Access Manager Go
👤 Sharing: AI
```go
package main
import (
"fmt"
"log"
"net/http"
"os"
"strings"
"github.com/joho/godotenv"
)
// APIKeyAuthMiddleware is a middleware that checks for a valid API key
func APIKeyAuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Retrieve the API key from the request header
apiKey := r.Header.Get("X-API-Key")
// Alternatively, retrieve the API key from a query parameter
// apiKey := r.URL.Query().Get("api_key")
if apiKey == "" {
// API key is missing
http.Error(w, "API Key Required", http.StatusUnauthorized)
return
}
// Get the allowed API keys from environment variables (or a database, config file, etc.)
allowedKeys := strings.Split(os.Getenv("ALLOWED_API_KEYS"), ",")
// Validate the API key
valid := false
for _, key := range allowedKeys {
if apiKey == key {
valid = true
break
}
}
if !valid {
// API key is invalid
http.Error(w, "Invalid API Key", http.StatusUnauthorized)
return
}
// API key is valid, proceed to the next handler
next.ServeHTTP(w, r)
})
}
// ExampleHandler is a handler that requires a valid API key
func ExampleHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "API Key is valid! Successfully accessed the protected resource.")
}
func main() {
// Load environment variables from .env file
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
}
// Check if ALLOWED_API_KEYS is set in the environment
allowedKeys := os.Getenv("ALLOWED_API_KEYS")
if allowedKeys == "" {
log.Fatal("ALLOWED_API_KEYS environment variable not set. Please define at least one valid API Key.")
}
// Create a handler for the example API endpoint
exampleHandler := http.HandlerFunc(ExampleHandler)
// Wrap the handler with the API key authentication middleware
protectedHandler := APIKeyAuthMiddleware(exampleHandler)
// Define the route and handler
http.Handle("/api/protected", protectedHandler)
// Start the server
fmt.Println("Server listening on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
```
**Explanation:**
1. **Package and Imports:**
- `package main`: Declares the package as `main`, indicating this is an executable program.
- `import (...)`: Imports necessary packages:
- `fmt`: For formatted input/output.
- `log`: For logging errors.
- `net/http`: For creating HTTP servers and handling requests.
- `os`: For interacting with the operating system (e.g., getting environment variables).
- `strings`: For string manipulation.
- `github.com/joho/godotenv`: A popular library for loading environment variables from a `.env` file (useful for storing API keys securely during development). If you don't have it, install it with `go get github.com/joho/godotenv`.
2. **`.env` file (Important for Security and Configuration)**
Create a file named `.env` in the same directory as your `main.go` file. This file will hold your API keys. Here's an example `.env` file:
```
ALLOWED_API_KEYS=mysecretapikey1,anothersecretkey,yetanother
```
**Important:** Never commit your `.env` file to version control (e.g., Git)! Add `.env` to your `.gitignore` file to prevent it from being uploaded. In production, you would typically set environment variables directly on your server (e.g., using `export ALLOWED_API_KEYS=...`).
3. **`APIKeyAuthMiddleware`:**
- This is the core of the API access manager. It's a middleware function that intercepts HTTP requests and checks for a valid API key.
- It takes an `http.Handler` as input (the next handler in the chain) and returns a new `http.Handler`.
- **Inside the middleware:**
- `r.Header.Get("X-API-Key")`: Retrieves the API key from the `X-API-Key` header. You could also check for the API key in a query parameter (using `r.URL.Query().Get("api_key")`). The header approach is generally preferred.
- **Error Handling (Missing API Key):** If the `apiKey` is empty, it means the API key is missing. The middleware sends an HTTP 401 Unauthorized error and returns.
- `os.Getenv("ALLOWED_API_KEYS")`: Retrieves the comma-separated list of allowed API keys from the `ALLOWED_API_KEYS` environment variable. This is read from the `.env` file when the `godotenv.Load()` function is called.
- `strings.Split(..., ",")`: Splits the comma-separated string of allowed API keys into a slice of strings.
- **API Key Validation:** The code iterates through the `allowedKeys` slice and checks if the `apiKey` from the request matches any of the allowed keys. If a match is found, `valid` is set to `true`, and the loop breaks.
- **Error Handling (Invalid API Key):** If `valid` is still `false` after the loop, it means the API key is invalid. The middleware sends an HTTP 401 Unauthorized error and returns.
- `next.ServeHTTP(w, r)`: If the API key is valid, the middleware calls the `ServeHTTP` method of the next handler in the chain (the `ExampleHandler` in this case). This allows the request to proceed to the actual API endpoint.
4. **`ExampleHandler`:**
- This is a simple handler function that would be called if the API key is valid.
- It simply writes a success message to the response. In a real application, this handler would perform some actual API logic.
5. **`main` Function:**
- `godotenv.Load()`: Loads environment variables from the `.env` file. This is crucial for reading the `ALLOWED_API_KEYS`.
- **Error Handling (Missing Environment Variable):** Checks if the `ALLOWED_API_KEYS` environment variable is set. If it's not, the program exits with an error message. This ensures that the program doesn't run without the required configuration.
- `http.HandlerFunc(ExampleHandler)`: Converts the `ExampleHandler` function into an `http.Handler`.
- `APIKeyAuthMiddleware(exampleHandler)`: Wraps the `exampleHandler` with the `APIKeyAuthMiddleware`. This creates a new handler that first checks the API key and then calls the `exampleHandler` if the key is valid.
- `http.Handle("/api/protected", protectedHandler)`: Registers the protected handler for the `/api/protected` route. Any requests to this route will now be intercepted by the middleware.
- `http.ListenAndServe(":8080", nil)`: Starts the HTTP server on port 8080.
**How to Run the Code:**
1. **Save:** Save the code as `main.go`.
2. **Create `.env`:** Create a `.env` file in the same directory as `main.go` and add at least one allowed API key: `ALLOWED_API_KEYS=your_api_key` (replace `your_api_key` with an actual key).
3. **Install `godotenv` (if you haven't already):** `go get github.com/joho/godotenv`
4. **Run:** `go run main.go`
**How to Test:**
1. **Successful Request (Valid API Key):**
```bash
curl -H "X-API-Key: your_api_key" http://localhost:8080/api/protected
```
(Replace `your_api_key` with the actual API key you put in your `.env` file.) You should see the message "API Key is valid! Successfully accessed the protected resource."
2. **Missing API Key:**
```bash
curl http://localhost:8080/api/protected
```
You should see an HTTP 401 Unauthorized error with the message "API Key Required".
3. **Invalid API Key:**
```bash
curl -H "X-API-Key: invalid_api_key" http://localhost:8080/api/protected
```
You should see an HTTP 401 Unauthorized error with the message "Invalid API Key".
**Improvements and Considerations:**
* **Database or Config File for API Keys:** Instead of storing API keys in environment variables (especially for production), use a database or a more robust configuration file (e.g., YAML, JSON). This allows for easier management and scaling of API keys.
* **API Key Generation:** Implement a mechanism for generating new API keys (e.g., using a cryptographically secure random number generator).
* **API Key Revocation:** Provide a way to revoke API keys if they are compromised.
* **Rate Limiting:** Add rate limiting to prevent abuse of your API.
* **Logging:** Log API requests and errors for auditing and debugging purposes.
* **Authentication/Authorization:** For more complex scenarios, consider using established authentication and authorization frameworks like OAuth 2.0 or JWT (JSON Web Tokens). These frameworks provide more features and security.
* **HTTPS:** Always use HTTPS (SSL/TLS) to encrypt the communication between the client and the server, especially when transmitting API keys.
* **Context:** Consider using Go's `context` package to pass request-scoped values (like the authenticated user) down the middleware chain.
This comprehensive example provides a solid foundation for building an API access manager in Go. Remember to adapt the code to your specific needs and security requirements.
👁️ Viewed: 5
Comments