1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
package main
import (
"encoding/base64"
"fmt"
"log"
"strings"
scrypt "github.com/elithrar/simple-scrypt"
"github.com/fasthttp/router"
"github.com/valyala/fasthttp"
)
// basicAuth returns the username and password provided in the request's
// Authorization header, if the request uses HTTP Basic Authentication.
// See RFC 2617, Section 2.
func basicAuth(ctx *fasthttp.RequestCtx) (username, password string, ok bool) {
auth := ctx.Request.Header.Peek("Authorization")
if auth == nil {
return
}
return parseBasicAuth(string(auth))
}
// parseBasicAuth parses an HTTP Basic Authentication string.
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
func parseBasicAuth(auth string) (username, password string, ok bool) {
const prefix = "Basic "
if !strings.HasPrefix(auth, prefix) {
return
}
c, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
if err != nil {
return
}
cs := string(c)
s := strings.IndexByte(cs, ':')
if s < 0 {
return
}
return cs[:s], cs[s+1:], true
}
// BasicAuth is the basic auth handler
func BasicAuth(h fasthttp.RequestHandler, requiredUser string, requiredPasswordHash []byte) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
// Get the Basic Authentication credentials
user, password, hasAuth := basicAuth(ctx)
// WARNING:
// DO NOT use plain-text passwords for real apps.
// A simple string comparison using == is vulnerable to a timing attack.
// Instead, use the hash comparison function found in your hash library.
// This example uses scrypt, which is a solid choice for secure hashing:
// go get -u github.com/elithrar/simple-scrypt
if hasAuth && user == requiredUser {
// Uses the parameters from the existing derived key. Return an error if they don't match.
err := scrypt.CompareHashAndPassword(requiredPasswordHash, []byte(password))
if err != nil {
// log error and request Basic Authentication again below.
log.Fatal(err)
} else {
// Delegate request to the given handle
h(ctx)
return
}
}
// Request Basic Authentication otherwise
ctx.Error(fasthttp.StatusMessage(fasthttp.StatusUnauthorized), fasthttp.StatusUnauthorized)
ctx.Response.Header.Set("WWW-Authenticate", "Basic realm=Restricted")
}
}
// Index is the index handler
func Index(ctx *fasthttp.RequestCtx) {
fmt.Fprint(ctx, "Not protected!\n")
}
// Protected is the Protected handler
func Protected(ctx *fasthttp.RequestCtx) {
fmt.Fprint(ctx, "Protected!\n")
}
func main() {
user := "gordon"
pass := "secret!"
// generate a hashed password from the password above:
hashedPassword, err := scrypt.GenerateFromPassword([]byte(pass), scrypt.DefaultParams)
if err != nil {
log.Fatal(err)
}
r := router.New()
r.GET("/", Index)
r.GET("/protected/", BasicAuth(Protected, user, hashedPassword))
log.Fatal(fasthttp.ListenAndServe(":8080", r.Handler))
}
|