Skip to content

Instantly share code, notes, and snippets.

@elithrar
Last active June 30, 2021 07:12
Show Gist options
  • Save elithrar/9146306 to your computer and use it in GitHub Desktop.
Save elithrar/9146306 to your computer and use it in GitHub Desktop.
HTTP Basic Auth example in Go (based on http://stackoverflow.com/a/21937924/556573 + bespoke middleware implementation)
package main
import (
"encoding/base64"
"github.com/gorilla/mux"
"net/http"
"strings"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/form", use(myHandler, basicAuth))
http.Handle("/", r)
http.ListenAndServe(":9900", nil)
}
// use provides a cleaner interface for chaining middleware for single routes.
// Middleware functions are simple HTTP handlers (w http.ResponseWriter, r *http.Request)
//
// r.HandleFunc("/login", use(loginHandler, rateLimit, csrf))
// r.HandleFunc("/form", use(formHandler, csrf))
// r.HandleFunc("/about", aboutHandler)
//
// See https://gist.github.com/elithrar/7600878#comment-955958 for how to extend it to suit simple http.Handler's
func use(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc {
for _, m := range middleware {
h = m(h)
}
return h
}
func myHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Authenticated!"))
return
}
// Leverages nemo's answer in http://stackoverflow.com/a/21937924/556573
func basicAuth(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
if len(s) != 2 {
http.Error(w, "Not authorized", 401)
return
}
b, err := base64.StdEncoding.DecodeString(s[1])
if err != nil {
http.Error(w, err.Error(), 401)
return
}
pair := strings.SplitN(string(b), ":", 2)
if len(pair) != 2 {
http.Error(w, "Not authorized", 401)
return
}
if pair[0] != "username" || pair[1] != "password" {
http.Error(w, "Not authorized", 401)
return
}
h.ServeHTTP(w, r)
}
}
@tp
Copy link

tp commented Jul 7, 2014

Line 65 is dangerous, in that either the correct username OR the correct password allow you to enter the page.

@abemedia
Copy link

abemedia commented Apr 5, 2016

Definitely! Should read if pair[0] != "username" || pair[1] != "password" instead.

@flavio-evocorp
Copy link

Acredito que a função poderia ser simplificada para:

return func(w http.ResponseWriter, r *http.Request) {

    w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)

    username, password, authOK := r.BasicAuth()
    if authOK == false {
        http.Error(w, "Not authorized", 401)
        return
    }

    if username != "username" || password != "password" {
        http.Error(w, "Not authorized", 401)
        return
    }

    h.ServeHTTP(w, r)
}

@elithrar
Copy link
Author

elithrar commented Oct 31, 2017

Updated to correct a (bad) bug (write tests for your software. Gists aren't so lucky!)

@xuelvming
Copy link

Greate post, Just have a concern,
w.Header().Set("WWW-Authenticate", Basic realm="Restricted")
this line, should it be unset when the user is authenticated , just before the
h.ServeHTTP(w, r)
https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
Seems there is no need to set the WWW-Authenticate after user has passed the authentication.

@rogierlommers
Copy link

Any idea how I can use your middleware with static fileserver? See here: https://stackoverflow.com/questions/50863974/gorilla-mux-middleware-for-static-fileserver

@mugli
Copy link

mugli commented Sep 15, 2018

There is a BasicAuth() method in the http.Request struct too which does the splitting and decoding stuff.
https://golang.org/pkg/net/http/#Request.BasicAuth

Usage:

func basicAuth(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        user, pass, _ := r.BasicAuth()
        
        if *flagUser != user || *flagPass != pass {
            w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
            http.Error(w, "Unauthorized.", http.StatusUnauthorized)
            return
        }

        h.ServeHTTP(w, r)
    })
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment