package server import ( "crypto/sha256" "crypto/subtle" "net/http" ) func (app *Application) BasicAuth(next http.HandlerFunc) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Extract the username and password from the request Authorization header. // If no Authentication header is present, or if the header value is // invalid, then the 'ok' return value will be false. username, password, ok := r.BasicAuth() if ok { // Calculate SHA-256 hashes for the provided and expected usernames // and passwords in order to have constant length values. actualUsernameHash := sha256.Sum256([]byte(username)) actualPasswordHash := sha256.Sum256([]byte(password)) expectedUsernameHash := sha256.Sum256([]byte(app.Username)) expectedPasswordHash := sha256.Sum256([]byte(app.Password)) // Use the subtle.ConstantTimeCompare() function to check if the // provided username and password hashes equal the expected // username and password hashes. // ConstantTimeCompare will return 1 if the values are equal, or 0 otherwise. // Importantly, we should do the work to evaluate both the username // and password before checking the return values, to avoid leaking information. usernameMatch := 1 == subtle.ConstantTimeCompare(actualUsernameHash[:], expectedUsernameHash[:]) passwordMatch := 1 == subtle.ConstantTimeCompare(actualPasswordHash[:], expectedPasswordHash[:]) // If the username and password are correct, then call the next // handler in the chain. Make sure to return afterwards, so that // none of the code below is run. if usernameMatch && passwordMatch { next.ServeHTTP(w, r) return } } // If the Authentication header is not present, is invalid, or the // username or password is wrong, then set a WWW-Authenticate header // to inform the client that we expect them to use basic authentication // and send a 401 Unauthorized response. w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) http.Error(w, "Please provide an authorization", http.StatusUnauthorized) }) }