Microservices in Go – Part 3 : Basic Authentication

Hey everyone, Welcome back! It’s been months we met in our blogging journey. But, we are back with a bang! In this blog we will add authentication to the very simple microservice we have developed in our previous blog. Let’s go ahead.

Authentication

Hmmm! What’s authentication and why do we need it in a microservice? Authentication, in our terms is to validate the identity of the incoming request on our microservice. Using authentication, we can limit the access to the people/software with valid credentials. In this post, we will implement very basic authentication where we use username and password to validate incoming client.

See the source image
Authentication Illustration

Let’s Code

As we are trying to add authentication to our microservice, let’s go ahead and add another handler in our handlers.go file. Add the following code to the file, then we will go through what’s in it.

func AuthHandler(h http.Handler) http.HandlerFunc {
	return func(rw http.ResponseWriter, r *http.Request) {
		user, pass, ok := r.BasicAuth()
		if ok {
			username := sha256.Sum256([]byte(os.Getenv("USER_NAME")))
			password := sha256.Sum256([]byte(os.Getenv("USER_PASS")))
			userHash := sha256.Sum256([]byte(user))
			passHash := sha256.Sum256([]byte(pass))
			validUser := subtle.ConstantTimeCompare(userHash[:],username[:]) == 1
			validPass := subtle.ConstantTimeCompare(passHash[:],password[:]) == 1
			if validPass && validUser{
			    h.ServeHTTP(rw,r)
				return
			}
		}
		http.Error(rw, "No/Invalid Credentials", http.StatusUnauthorized)
	}
}

We have defined an HTTP handler, which takes another http handler as input, unlike our previous one’s. We will get to know about this in a while. Keeping that aside, we will focus on the functionality of this handler.

We read the username and password from the incoming request using r.BasicAuth method by standard httpRequest, which gives us the username, password coming from the request if present. The ok variable tells us if the credentials are provided or not.

Now, we have the username and password coming from the request. We will compare them with what we have configured in the environment. We did comparison using subtle.ConstantTimeCompare method.

Note: We did use sublte.ConstantTimeCompare instead of the normal comparison using == operator returns not equal on the first invalid match between strings. This helps the attackers in knowing the number of characters matched by making use of time taken to reject the request.

We used the sha256 hash, as the sublte.ConstantTimeCompare might leak length of the string when used directly.

Now let’s go ahead and add this AuthHandler as a pre-handler for all of our previously defined handlers. Open main.go file and replace the existing code with following.

package main

import (
	"fmt"
	"net/http"

	"github.com/HelloWorld/goProductAPI/handlers"
	"github.com/gorilla/mux"
)

func main() {
	// Create new Router
	router := mux.NewRouter()
	// route properly to respective handlers
	router.Handle("/products", handlers.GetProductsHandler()).Methods("GET")
	router.Handle("/products", handlers.CreateProductHandler()).Methods("POST")
	router.Handle("/products/{id}", handlers.GetProductHandler()).Methods("GET")
	router.Handle("/products/{id}", handlers.DeleteProductHandler()).Methods("DELETE")
	router.Handle("/products/{id}", handlers.UpdateProductHandler()).Methods("PUT")

	// Create new server and assign the router
	server := http.Server{
		Addr:    ":9090",
		Handler: handlers.AuthHandler(router),
	}
	fmt.Println("Staring Product Catalog server on Port 9090")
	// Start Server on defined port/host.
	server.ListenAndServe()
}

The change to our previous code happens on line 24, where we pass our mux router as a parameter to our AuthHandler which is used as the primary handler of our HTTP server.

Boom 💣💣. We have added Basic Authentication to our existing microservice. Let’s go ahead and test this.

See the source image

Testing Time

Go to Postman and create a new request. In the authorization tab, choose the Basic Auth and fill in the username and password with the one’s you have configured in your environment.

Authentication Successful

If the entered credentials are valid, you will see the response as in the screenshot, else you might see the below 👇

Failed Authentication

Note: Check if you have configured the USER_NAME and USER_PASS environment variables before running our service using the below command.

go run ./main.go

Hmm! That’s it for todays blog. We have seen how to implement very basic authentication in a Golang Microservice. Here’s the link to our GitHub repo for the complete code. Ok!, so how can I restrict access on a specific handler but allow on the other based on credentials?

Well that’s a good question and we will answer that in our upcoming blog. Until then, stay safe, stay tuned. Cheers ✌.

Blogs in this Series

4 responses to “Microservices in Go – Part 3 : Basic Authentication”

Leave a comment

Design a site like this with WordPress.com
Get started