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