Skip to content
Santekno.com | Level Up Your Engineering Skills
EN
📖 0%
04 Feb 2025 · 4 min read ·Article 75 / 119
Go

16 Building HMAC Authentication Middleware Using `httprouter` in Golang

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

In this article, we will discuss how to build an HMAC (Hash-based Message Authentication Code) authentication middleware using Go and the httprouter library. This middleware functions to authenticate HTTP requests based on an HMAC signature, ensuring data integrity and verifying that the data originates from a trusted source.

What is HMAC?

HMAC is a method used to provide authentication and integrity to messages or data transmitted over a network. In the context of APIs, HMAC is used to verify that the received data has not been modified in transit and that it was indeed sent by an authorized sender.

HMAC works by using a hashing algorithm (e.g., SHA-256) combined with a secret key. This process generates a signature that can be verified by the recipient. If the signature matches, the data is considered valid.

Project Setup

To get started, ensure you have a Go development environment set up. You also need to install httprouter by running:

bash
1go get github.com/julienschmidt/httprouter

Next, create a new project folder and add a main.go file inside it.

Step 1: Implementing HMAC Middleware

The middleware is responsible for inspecting incoming requests, verifying the HMAC signature, and ensuring that the request comes from a legitimate source. Below is the basic implementation:

go
 1package main
 2
 3import (
 4	"crypto/hmac"
 5	"crypto/sha256"
 6	"encoding/hex"
 7	"fmt"
 8	"io/ioutil"
 9	"net/http"
10	"strings"
11
12	"github.com/julienschmidt/httprouter"
13)
14
15// Secret key for HMAC verification
16var secretKey = []byte("your-secret-key")
17
18// HMAC Middleware
19func HMACMiddleware(next httprouter.Handle) httprouter.Handle {
20	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
21		// Retrieve signature from the "X-Signature" header
22		signature := r.Header.Get("X-Signature")
23		if signature == "" {
24			http.Error(w, "Missing signature", http.StatusUnauthorized)
25			return
26		}
27
28		// Read request body to generate HMAC signature
29		body, err := ioutil.ReadAll(r.Body)
30		if err != nil {
31			http.Error(w, "Unable to read body", http.StatusInternalServerError)
32			return
33		}
34
35		// Create HMAC using the secret key
36		mac := hmac.New(sha256.New, secretKey)
37		mac.Write(body)
38		expectedSignature := hex.EncodeToString(mac.Sum(nil))
39
40		// Verify if the signature matches
41		if !hmac.Equal([]byte(signature), []byte(expectedSignature)) {
42			http.Error(w, "Invalid signature", http.StatusUnauthorized)
43			return
44		}
45
46		// Proceed to the next handler
47		next(w, r, ps)
48	}
49}

Step 2: Setting Up Router and Handlers

Next, set up the httprouter router and a handler to process requests successfully authenticated via HMAC:

go
 1func HelloHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
 2	fmt.Fprintln(w, "Hello, authenticated user!")
 3}
 4
 5func main() {
 6	router := httprouter.New()
 7
 8	// Apply HMAC middleware to this endpoint
 9	router.GET("/hello", HMACMiddleware(HelloHandler))
10
11	// Start the server
12	http.ListenAndServe(":8080", router)
13}

Here, we add a /hello route protected by the HMAC middleware. Only requests with a valid signature will be granted access.

Step 3: Testing HMAC with Unit Tests

To ensure our HMAC middleware works correctly, we will write unit tests covering two scenarios:

  1. A legitimate request with a valid signature.
  2. A request with an invalid signature.

Here is the unit test code:

go
 1package main
 2
 3import (
 4	"net/http"
 5	"net/http/httptest"
 6	"testing"
 7	"strings"
 8	"crypto/hmac"
 9	"crypto/sha256"
10	"encoding/hex"
11
12	"github.com/julienschmidt/httprouter"
13	"github.com/stretchr/testify/assert"
14)
15
16// Function to create a request with HMAC header
17func makeRequest(signature string, body string) *http.Request {
18	req := httptest.NewRequest("GET", "/hello", strings.NewReader(body))
19	req.Header.Set("X-Signature", signature)
20	return req
21}
22
23// Unit Test for HMAC Middleware
24func TestHMACMiddleware(t *testing.T) {
25	// Create router and handler for testing
26	router := httprouter.New()
27	router.GET("/hello", HMACMiddleware(HelloHandler))
28
29	// Test case 1: Valid request
30	body := "Hello World"
31	mac := hmac.New(sha256.New, secretKey)
32	mac.Write([]byte(body))
33	validSignature := hex.EncodeToString(mac.Sum(nil))
34	req := makeRequest(validSignature, body)
35
36	// Simulate request
37	rr := httptest.NewRecorder()
38	router.ServeHTTP(rr, req)
39
40	// Verify response
41	assert.Equal(t, http.StatusOK, rr.Code)
42	assert.Contains(t, rr.Body.String(), "Hello, authenticated user!")
43
44	// Test case 2: Invalid signature
45	invalidSignature := "invalid-signature"
46	req = makeRequest(invalidSignature, body)
47
48	// Simulate request
49	rr = httptest.NewRecorder()
50	router.ServeHTTP(rr, req)
51
52	// Verify response
53	assert.Equal(t, http.StatusUnauthorized, rr.Code)
54	assert.Contains(t, rr.Body.String(), "Invalid signature")
55}

Running the Unit Tests

To run the unit tests, execute:

bash
1go test -v

Conclusion

In this article, we learned how to build HMAC authentication middleware using Go and httprouter. This middleware ensures data integrity and authentication for HTTP requests. We also explored unit testing to verify its functionality.

For more in-depth Go tutorials, visit Tutorial Golang . If you want to learn more about HTTP routers in Go, check out Web HTTP Router in Golang .

By understanding HMAC concepts and their implementation in middleware, you can build more secure and reliable systems. Hope this article was helpful and easy to follow!

Related Articles

💬 Comments