Last active
August 29, 2015 14:18
-
-
Save Tantas/61d4ff467eab05633c09 to your computer and use it in GitHub Desktop.
Golang Basic Auth Handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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