Skip to content

Instantly share code, notes, and snippets.

@Tantas
Last active August 29, 2015 14:18
Show Gist options
  • Save Tantas/61d4ff467eab05633c09 to your computer and use it in GitHub Desktop.
Save Tantas/61d4ff467eab05633c09 to your computer and use it in GitHub Desktop.
Golang Basic Auth Handler
var alphaNumeric = regexp.MustCompile("^[a-zA-Z0-9]*$")
var db *sql.DB
// Checks that the user is authorized by validating the username and password
// provided through basic authentication against values in the database.
// Presents a login page if not present or invalid.
func authHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Send a 401 Not Authorized and set a header value to trigger login.
presentLogin := func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("WWW-Authenticate", `Basic realm="<place realm here>"`)
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Unauthorized."))
}
// Show login page if missing HTTP Basic Authentication header.
authHeader := r.Header["Authorization"] // ex. [Basic dXNlcm5hbWU6cGFzc3dvcmQ=]
if len(authHeader) != 1 {
presentLogin(w, r)
return
}
// Remove the 'Basic ' prefix on the string.
schemeAndCreds := strings.SplitN(authHeader[0], "Basic ", 2)
if len(schemeAndCreds) != 2 {
presentLogin(w, r)
return
}
// Base64 decode the credentials.
encodedCreds := schemeAndCreds[1] // ex. dXNlcm5hbWU6cGFzc3dvcmQ=
decodedCreds, err := base64.StdEncoding.DecodeString(encodedCreds)
if err != nil {
log.Println(err.Error())
presentLogin(w, r)
return
}
// Split the string to isolate the username and password.
creds := fmt.Sprintf("%q", decodedCreds) // ex. username:password
usernameAndPassword := strings.SplitN(creds, ":", 2)
if len(usernameAndPassword) != 2 {
presentLogin(w, r)
return
}
// Restrict the usernames and passwords to be alphanumeric for security.
username := strings.TrimPrefix(usernameAndPassword[0], `"`)
password := strings.TrimSuffix(usernameAndPassword[1], `"`)
password = strings.TrimPrefix(password, `:`)
if !(alphaNumeric.MatchString(username)) {
presentLogin(w, r)
return
}
if !alphaNumeric.MatchString(password) {
presentLogin(w, r)
return
}
// Validate the user exists Prepared Statement.
stmt, err := db.Prepare(`select count(username) from users
where username = ? and password = ?`)
if err != nil {
log.Println(err.Error())
http.Error(w, "Internal Error", http.StatusInternalServerError)
return
}
defer stmt.Close()
// Check if the username and password combination exist.
row, err := stmt.Query(username, password)
if err != nil {
log.Println(err.Error())
http.Error(w, "Internal Error", http.StatusInternalServerError)
return
}
var count int
row.Next()
err = row.Scan(&count)
if err != nil {
log.Println(err.Error())
http.Error(w, "Internal Error", http.StatusInternalServerError)
return
}
if count != 1 {
presentLogin(w, r)
return
}
// Forward the request.
fn(w, r)
}
}
func main() {
var err error
db, err = sql.Open("driver", "username:password@/schema")
if err != nil {
log.Fatalf("Error initializing db driver: %s", err.Error())
}
defer db.Close()
db.SetMaxIdleConns(100)
// Open the database connection and check accessibilty.
err = db.Ping()
if err != nil {
log.Fatalf("Error on opening database connection: %s", err.Error())
}
http.HandleFunc("/", authorizationHandler(viewHandler))
http.ListenAndServe(":8000", nil)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment