mirror of
https://github.com/coalaura/whiskr.git
synced 2025-09-08 08:39:53 +00:00
authentication
This commit is contained in:
125
auth.go
Normal file
125
auth.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type AuthenticationRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (u *EnvUser) Signature(secret string) []byte {
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
|
||||
mac.Write([]byte(u.Password))
|
||||
mac.Write([]byte(u.Username))
|
||||
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
func (e *Environment) Authenticate(username, password string) *EnvUser {
|
||||
user, ok := e.Authentication.lookup[username]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
func (e *Environment) SignAuthToken(user *EnvUser) string {
|
||||
signature := user.Signature(e.Tokens.Secret)
|
||||
|
||||
return user.Username + ":" + hex.EncodeToString(signature)
|
||||
}
|
||||
|
||||
func (e *Environment) VerifyAuthToken(token string) bool {
|
||||
index := strings.Index(token, ":")
|
||||
if index == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
username := token[:index]
|
||||
|
||||
user, ok := e.Authentication.lookup[username]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
signature, err := hex.DecodeString(token[index+1:])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
expected := user.Signature(e.Tokens.Secret)
|
||||
|
||||
return hmac.Equal(signature, expected)
|
||||
}
|
||||
|
||||
func IsAuthenticated(r *http.Request) bool {
|
||||
if !env.Authentication.Enabled {
|
||||
return true
|
||||
}
|
||||
|
||||
cookie, err := r.Cookie("whiskr_token")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return env.VerifyAuthToken(cookie.Value)
|
||||
}
|
||||
|
||||
func Authenticate(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !IsAuthenticated(r) {
|
||||
RespondJson(w, http.StatusUnauthorized, map[string]any{
|
||||
"error": "unauthorized",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func HandleAuthentication(w http.ResponseWriter, r *http.Request) {
|
||||
var request AuthenticationRequest
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
RespondJson(w, http.StatusBadRequest, map[string]any{
|
||||
"error": "missing username or password",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
user := env.Authenticate(request.Username, request.Password)
|
||||
if user == nil {
|
||||
RespondJson(w, http.StatusUnauthorized, map[string]any{
|
||||
"error": "invalid username or password",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "whiskr_token",
|
||||
Value: env.SignAuthToken(user),
|
||||
})
|
||||
|
||||
RespondJson(w, http.StatusOK, map[string]any{
|
||||
"authenticated": true,
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user