package auth import ( "log/slog" "net/http" ) type Handler struct { validator *Validator logger *slog.Logger } func NewHandler(validator *Validator, logger *slog.Logger) *Handler { if logger == nil { logger = slog.Default() } return &Handler{ validator: validator, logger: logger, } } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/healthz" { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("ok")) return } authHeader := r.Header.Get("Authorization") token, err := ParseBearerToken(authHeader) if err != nil { h.logAttempt(r, "denied", "auth_header_invalid", "") h.unauthorized(w) return } fingerprint := TokenFingerprint(token) if !h.validator.IsValid(token) { h.logAttempt(r, "denied", "token_invalid", fingerprint) h.unauthorized(w) return } h.logAttempt(r, "allowed", "token_valid", fingerprint) w.WriteHeader(http.StatusOK) } func (h *Handler) unauthorized(w http.ResponseWriter) { w.Header().Set("WWW-Authenticate", "Bearer") w.WriteHeader(http.StatusUnauthorized) } func (h *Handler) logAttempt(r *http.Request, result, reason, fingerprint string) { attrs := []any{ "result", result, "reason", reason, "method", r.Method, "path", r.URL.Path, } if fingerprint != "" { attrs = append(attrs, "token_fingerprint", fingerprint) } h.logger.Info("auth attempt", attrs...) }