How I built an Authentication API in Go

Search for a command to run...

No comments yet. Be the first to comment.
If you have been using Terraform for more than five minutes, you would have come across variables and replacing default values for instances using a .tfvars file. Below is a snippet of replacing default values for an EC2 Instance using Terraform: ec2...

For work, I have had an interesting project where I had to pass through multiple AWS Backup Plans into a Python class to make all my Backup Plans; these backup plans had so many options that needed configuring that the most logical way of passing the...

Introduction Managing AWS resources efficiently is a critical challenge for cloud architects and developers, especially using a dynamic language like Python. While Python offers flexibility and rapid development capabilities, its lack of built-in typ...

Introduction When designing a scalable and flexible architecture on AWS, it's common to use an Application Load Balancer (ALB) to distribute traffic across multiple targets, such as EC2 instances and ECS tasks. However, a challenge arises when you mu...

How Bash Saved the Day: Deploying Helm Charts with a Bash CLI Program In the world of software development, encountering roadblocks is part of the journey. Recently, I faced a significant challenge while trying to deploy a Helm chart to a Kubernetes ...

In the world of web development, secure communication between client and server is paramount. This was the need of a client of mine, a startup called Greencloud; check them out here: https://www.greencloudcomputing.io/. This API is for a bigger project that I am working on with them. I will hopefully get to blog about it one day soon.
One common way to secure these communications is through the use of authentication mechanisms. Today, I'll walk you through creating a custom Authentication API in Go, providing a flexible and reusable way to handle different authentication methods.
Our goal is to create a middleware-based HTTP client that can handle various types of authentication, such as username/password and API key authentication. We'll leverage Go's powerful type system and interfaces to achieve this.
Here's the code we'll be working with:
package main
import (
"context"
"fmt"
"io"
"net/http"
)
// HTTPClient is an interface that can be used to perform HTTP requests
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}
// HTTPClientFunc is a function that implements HTTPClient
type HTTPClientFunc func(req *http.Request) (*http.Response, error)
// Do implements HTTPClient
func (fn HTTPClientFunc) Do(req *http.Request) (*http.Response, error) {
return fn(req)
}
// Middleware is a function that can mutate the request before it is sent
type Middleware func(HTTPClient) HTTPClient
// UserPassAuthentication is middleware that adds username/password authentication
// to the request
func UserPassAuthentication(username, password string) Middleware {
return func(client HTTPClient) HTTPClient {
return HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
req.Header.Set("Authorization", "Bearer "+username+password)
return client.Do(req)
})
}
}
// APIKeyAuthentication is middleware that adds APIKey-based authentication to requests
func APIKeyAuthentication(apikey string) Middleware {
return func(client HTTPClient) HTTPClient {
return HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
req.Header.Set("Authorization", "Bearer "+apikey)
return client.Do(req)
})
}
}
type Client struct {
client HTTPClient
}
func GCNewClient(httpClient *http.Client, middleware ...Middleware) Client {
client := Client{client: httpClient}
// Apply the middleware
for _, m := range middleware {
client.client = m(client.client)
}
return client
}
func (c *Client) Get(ctx context.Context, GCUrl string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, "GET", GCUrl, nil)
if err != nil {
return nil, err
}
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read http response body: %w", err)
}
fmt.Println(string(body))
return body, err
}
func main() {
// Replace with your API key
apiKey := "your-api-key-here"
// Replace with the API endpoint
url := "https://your-api-endpoint.com"
client := GCNewClient(http.DefaultClient, APIKeyAuthentication(apiKey))
client.Get(context.TODO(), url)
}
We start by defining an HTTPClient interface and a function type HTTPClientFunc that implements this interface. This allows us to create flexible middleware that can modify requests before they are sent.
We then define two middleware functions:
UserPassAuthentication: Adds a username and password to the request header.
APIKeyAuthentication: Adds an API key to the request header.
These middleware functions return a new HTTPClient that modifies the request headers as needed.
The Client Struct holds an HTTPClient, and the GCNewClient Function applies the provided middleware to this client.
The Get Method constructs an HTTP GET request and uses the client to execute it. It reads the response body and returns it.
In the main Function, we create a new client with API key authentication and use it to make a GET request to a specified endpoint. Remember to replace the placeholders with your actual API key and endpoint.
This approach provides a clean and modular way to handle different authentication mechanisms in Go. By using middleware, we can easily add or modify authentication methods without changing the core logic of our HTTP client.
Feel free to use and adapt this code for your projects. Happy coding!
I would like to thank Adam Talbot from the Kubernetes Go Community for helping with this project. Without his help, I would still be stuck looking at the NET/HTTP Package. You can find him here: https://uk.linkedin.com/in/mrtalbot
Please ensure you replace sensitive information, such as actual API keys, with placeholders before sharing your code publicly.