103 lines
2.0 KiB
Go
103 lines
2.0 KiB
Go
package middleware
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"source.toby3d.me/toby3d/auth/internal/common"
|
|
)
|
|
|
|
type (
|
|
BasicAuthConfig struct {
|
|
Skipper Skipper
|
|
Validator BasicAuthValidator
|
|
Realm string
|
|
}
|
|
|
|
BasicAuthValidator func(w http.ResponseWriter, r *http.Request, login, password string) (bool, error)
|
|
)
|
|
|
|
const DefaultRealm string = "Restricted"
|
|
|
|
const basic string = "basic"
|
|
|
|
//nolint:gochecknoglobals
|
|
var DefaultBasicAuthConfig = BasicAuthConfig{
|
|
Skipper: DefaultSkipper,
|
|
Realm: DefaultRealm,
|
|
}
|
|
|
|
func BasicAuth(validator BasicAuthValidator) Interceptor {
|
|
cfg := DefaultBasicAuthConfig
|
|
cfg.Validator = validator
|
|
|
|
return BasicAuthWithConfig(cfg)
|
|
}
|
|
|
|
func BasicAuthWithConfig(config BasicAuthConfig) Interceptor {
|
|
// Defaults
|
|
if config.Validator == nil {
|
|
panic("middleware: basic-auth middleware requires a validator function")
|
|
}
|
|
|
|
if config.Skipper == nil {
|
|
config.Skipper = DefaultBasicAuthConfig.Skipper
|
|
}
|
|
|
|
if config.Realm == "" {
|
|
config.Realm = DefaultRealm
|
|
}
|
|
|
|
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
|
if config.Skipper(w, r) {
|
|
next(w, r)
|
|
|
|
return
|
|
}
|
|
|
|
auth := r.Header.Get(common.HeaderAuthorization)
|
|
l := len(basic)
|
|
|
|
if len(auth) > l+1 && strings.EqualFold(string(auth[:l]), basic) {
|
|
b, err := base64.StdEncoding.DecodeString(string(auth[l+1:]))
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
}
|
|
|
|
cred := string(b)
|
|
for i := 0; i < len(cred); i++ {
|
|
if cred[i] == ':' {
|
|
// NOTE(toby3d): verify credentials.
|
|
valid, err := config.Validator(w, r, cred[:i], cred[i+1:])
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
return
|
|
}
|
|
|
|
if valid {
|
|
next(w, r)
|
|
|
|
return
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
realm := DefaultRealm
|
|
if config.Realm != DefaultRealm {
|
|
realm = strconv.Quote(config.Realm)
|
|
}
|
|
|
|
// NOTE(toby3d): require 401 status for login pop-up.
|
|
w.Header().Set(common.HeaderWWWAuthenticate, basic+" realm="+realm)
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
}
|
|
}
|