hsa-app/internal/web/middleware.go
Jean-Michel Tremblay 8b7252dad4 Initial commit: HSA receipt tracker
Go app for capturing and archiving HSA-eligible receipts: OIDC/PKCE auth
against Authelia, SQLite storage with dual-write (filesystem + DB blob),
mobile-first upload, and DB export.

Adds AI receipt classification: a config.json catalog of people and
categories (seeded into the DB on startup), a prompt builder that derives
name-order/initial variants from the data (with same-surname ambiguity
handling), and an Anthropic tool-use client behind POST /classify. Tests
run against a mock endpoint; a live integration test is env-gated to the
cheapest model.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:40:12 -04:00

63 lines
1.5 KiB
Go

package web
import (
"context"
"net/http"
"time"
"maisym.com/hsa/internal/auth"
)
type ctxKey int
const sessionKey ctxKey = 0
// requireAuth rejects requests without a valid session cookie by redirecting to /login.
// On success the decoded session is placed in the request context.
func (s *Server) requireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie(sessionCookie)
if err != nil {
http.Redirect(w, r, "/login", http.StatusFound)
return
}
sess, err := auth.DecodeSession(c.Value, s.cfg.SessionKey)
if err != nil {
s.clearCookie(w, sessionCookie)
http.Redirect(w, r, "/login", http.StatusFound)
return
}
ctx := context.WithValue(r.Context(), sessionKey, sess)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// sessionFrom returns the session stored in the context by requireAuth.
func sessionFrom(ctx context.Context) auth.Session {
sess, _ := ctx.Value(sessionKey).(auth.Session)
return sess
}
func (s *Server) setCookie(w http.ResponseWriter, name, value string, ttl time.Duration) {
http.SetCookie(w, &http.Cookie{
Name: name,
Value: value,
Path: "/",
MaxAge: int(ttl.Seconds()),
HttpOnly: true,
Secure: s.cfg.SecureCookies,
SameSite: http.SameSiteLaxMode,
})
}
func (s *Server) clearCookie(w http.ResponseWriter, name string) {
http.SetCookie(w, &http.Cookie{
Name: name,
Value: "",
Path: "/",
MaxAge: -1,
HttpOnly: true,
Secure: s.cfg.SecureCookies,
SameSite: http.SameSiteLaxMode,
})
}