Files
gotermix/internals/auth.go
T

95 lines
2.3 KiB
Go
Raw Normal View History

package internals
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/http"
"strings"
"time"
)
2026-05-24 07:18:54 +00:00
// ── CSRF ──────────────────────────────────────────────────────────────
func newCSRFToken() string {
b := make([]byte, 24)
rand.Read(b) //nolint:errcheck
return hex.EncodeToString(b)
}
// setCSRFCookie writes a fresh CSRF token to a short-lived cookie and returns
// the token value so it can be embedded in the rendered HTML form.
func setCSRFCookie(w http.ResponseWriter) string {
tok := newCSRFToken()
http.SetCookie(w, &http.Cookie{
Name: csrfCookieName,
Value: tok,
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
MaxAge: 900, // 15 min — covers slow typists
})
return tok
}
// checkCSRF returns true iff the submitted csrf_token form field matches the
// cookie value (constant-time compare to prevent timing side-channels).
func checkCSRF(r *http.Request) bool {
c, err := r.Cookie(csrfCookieName)
if err != nil || c.Value == "" {
return false
}
formTok := r.FormValue("csrf_token")
if formTok == "" {
return false
}
return hmac.Equal([]byte(c.Value), []byte(formTok))
}
func checkCreds(username, password string) bool {
if username != appCreds.Username {
return false
}
got := hashPassword(password, appCreds.Salt)
return hmac.Equal([]byte(got), []byte(appCreds.Hash))
}
func initAuthSecret() {
authSecret = make([]byte, 32)
rand.Read(authSecret)
}
func makeAuthToken() string {
ts := fmt.Sprintf("%d", time.Now().Unix())
mac := hmac.New(sha256.New, authSecret)
mac.Write([]byte(ts))
return ts + "." + hex.EncodeToString(mac.Sum(nil))
}
func validAuthToken(token string) bool {
dot := strings.LastIndex(token, ".")
if dot < 0 {
return false
}
ts, sig := token[:dot], token[dot+1:]
mac := hmac.New(sha256.New, authSecret)
mac.Write([]byte(ts))
if !hmac.Equal([]byte(sig), []byte(hex.EncodeToString(mac.Sum(nil)))) {
return false
}
var t int64
fmt.Sscanf(ts, "%d", &t)
return time.Since(time.Unix(t, 0)) < authTokenTTL
}
func isAuthed(r *http.Request) bool {
if nopwMode {
return true
}
c, err := r.Cookie(authCookieName)
return err == nil && validAuthToken(c.Value)
}