Files
Vetting/internal/auth/session.go
T
josh 9bb4b09a04
CI / Lint + build + test (push) Has been cancelled
Initial commit: full Phases 1-6 implementation
Post-repair hardware validation pipeline for Proxmox cluster hosts.
Go orchestrator + in-image agent + mkosi live image + bundled dnsmasq
PXE + SQLite + HTMX/SSE UI + notify registry + janitor + full docs.
2026-04-17 21:32:10 -04:00

101 lines
2.2 KiB
Go

package auth
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"golang.org/x/crypto/bcrypt"
)
const cookieName = "vetting_session"
type Manager struct {
PasswordHash string
Secret []byte
TTL time.Duration
}
func (m *Manager) VerifyPassword(password string) bool {
if m.PasswordHash == "" {
return false
}
return bcrypt.CompareHashAndPassword([]byte(m.PasswordHash), []byte(password)) == nil
}
// Issue writes a signed session cookie valid for m.TTL.
func (m *Manager) Issue(w http.ResponseWriter, r *http.Request) {
expiry := time.Now().Add(m.TTL).Unix()
payload := strconv.FormatInt(expiry, 10)
sig := m.sign(payload)
value := payload + "." + sig
http.SetCookie(w, &http.Cookie{
Name: cookieName,
Value: value,
Path: "/",
HttpOnly: true,
Secure: r.TLS != nil,
SameSite: http.SameSiteLaxMode,
Expires: time.Unix(expiry, 0),
})
}
func (m *Manager) Clear(w http.ResponseWriter) {
http.SetCookie(w, &http.Cookie{
Name: cookieName,
Value: "",
Path: "/",
HttpOnly: true,
MaxAge: -1,
})
}
var errInvalidSession = errors.New("invalid session")
// Validate returns nil if the request's cookie is present, signed, and not expired.
func (m *Manager) Validate(r *http.Request) error {
c, err := r.Cookie(cookieName)
if err != nil {
return errInvalidSession
}
parts := strings.SplitN(c.Value, ".", 2)
if len(parts) != 2 {
return errInvalidSession
}
payload, sig := parts[0], parts[1]
expected := m.sign(payload)
if !hmac.Equal([]byte(sig), []byte(expected)) {
return errInvalidSession
}
expiry, err := strconv.ParseInt(payload, 10, 64)
if err != nil {
return errInvalidSession
}
if time.Now().Unix() >= expiry {
return errInvalidSession
}
return nil
}
func (m *Manager) sign(payload string) string {
mac := hmac.New(sha256.New, m.Secret)
_, _ = mac.Write([]byte(payload))
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}
// BcryptHash is a helper used by the gen-admin-password tool.
func BcryptHash(password string) (string, error) {
b, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", fmt.Errorf("bcrypt: %w", err)
}
return string(b), nil
}