David.dev

HTTP Basic Authentication in go


There are a lot of solution to protect the admin of your web project, sessions, tokens etc. but a method that still works very well, especially for smaller projects, is http basic authentication. As a caveat your website should use https because http basic credentials are sent from the browser in clear so a secure connection ensures that the traffic in transit is secure.

Several tutorials I found online omit some important parts: where to store the admin username and password. Often they simply hardcode username and passwords which is a big security issue e.g. whenever you push your code to git (which you should if you want to maintain some order in your coding) these will be visible to whomever gets access to the repository.

I have also found tutorials that do not show how to protect your http routes. 

Go allows you to implement http basic authentication without any external package which is great.

Let's start with the username and password. You should store them as an environmental variable. If you are using a mac your default shell will be ZSH so you need to add your username and password to .zshRC. If you use BASH, often the default shell on linux, you add these credentials to .bashrc.

with your preferred editor editor or create the .bashrc or .zshrc file and add the credentials:

export GOADMINUSER=YourAdminUsername
export GOADMINPASSWORD="your long difficult to remember passphrase"

 save the file and if you want to use it in the same shell session pass the command

source .bashrc 

or .zshrc depending on which shell you use.

I have intentionally definited the username and password with goadminuser and goadminpassword to avoid possible conflicts with other environmental variables. You can of course user whatever you prefer. Note that the password (I recommend to use a passphrase with spaces is between quotation marks else it won't work with spaces.

Now for the go part

import (
	"fmt"
	"os"

)

// you need to import OS


func IsAllowed(w http.ResponseWriter, r *http.Request) bool {
	u, p, ok := r.BasicAuth()
	user := os.Getenv("GOADMINUSER")
	pw := os.Getenv("GOADMINPASSWORD")


	if !ok {
		w.Header().Set("WWW-Authenticate", `Basic realm="Beware! Protected REALM! "`)
		w.WriteHeader(401)
		w.Write([]byte("401 Unauthorized\n"))
		return false
	}

	if u != user || p != pw {
		w.Header().Set("WWW-Authenticate", `Basic realm="Beware! Protected REALM! "`)
		w.WriteHeader(401)
		w.Write([]byte("401 Unauthorized\n"))
		return false
	}

	return true
}

now to protect a route you add a check:

	http.HandleFunc("/admin_console", func(w http.ResponseWriter, r *http.Request) {
		if !IsAllowed(w, r) {
			return
		}....


// so if IsAllowed check is not passed it will not continue and display the unauthorized error defined in the IsAllowed function


in this example for the route /admin_console we check if !IsAllowed check has been passed. If not the admin route will not be executed and displayed. 


16

made with ❤ī¸ by david.dev 2024 RSS Feed