twitch-bot/authMiddleware.go
2024-05-24 13:59:29 +02:00

88 lines
2 KiB
Go

package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
"net/http"
"strings"
"github.com/gofrs/uuid/v3"
"github.com/pkg/errors"
"golang.org/x/crypto/argon2"
)
const (
// OWASP recommendations - 2023-07-07
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
argonFmt = "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s"
argonHashLen = 16
argonMemory = 46 * 1024
argonSaltLength = 8
argonThreads = 1
argonTime = 1
)
func fillAuthToken(token *configAuthToken) error {
token.Token = uuid.Must(uuid.NewV4()).String()
salt := make([]byte, argonSaltLength)
if _, err := rand.Read(salt); err != nil {
return errors.Wrap(err, "reading salt")
}
token.Hash = fmt.Sprintf(
argonFmt,
argon2.Version,
argonMemory, argonTime, argonThreads,
base64.RawStdEncoding.EncodeToString(salt),
base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(token.Token), salt, argonTime, argonMemory, argonThreads, argonHashLen)),
)
return nil
}
func writeAuthMiddleware(h http.Handler, module string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, pass, hasBasicAuth := r.BasicAuth()
var token string
switch {
case hasBasicAuth && pass != "":
token = pass
case r.Header.Get("Authorization") != "":
var (
tokenType string
hadPrefix bool
)
tokenType, token, hadPrefix = strings.Cut(r.Header.Get("Authorization"), " ")
switch {
case !hadPrefix:
// Legacy: Accept `Authorization: tokenhere`
token = tokenType
case strings.EqualFold(tokenType, "token"):
// This is perfect: `Authorization: Token tokenhere`
default:
// That was unexpected: `Authorization: Bearer tokenhere` or similar
http.Error(w, "invalid token type", http.StatusForbidden)
return
}
default:
http.Error(w, "auth not successful", http.StatusForbidden)
return
}
err := authService.ValidateTokenFor(token, module)
if err != nil {
http.Error(w, "auth not successful", http.StatusForbidden)
return
}
h.ServeHTTP(w, r)
})
}