package httpauth import ( "context" "net/http" "path" "strings" "firebase.google.com/go/v4/auth" httpauthmw "code.nochebuena.dev/go/httpauth" ) // TokenVerifier abstracts Firebase JWT verification. // *auth.Client satisfies this interface directly — no adapter needed in production. // Retained solely for unit-test mockability. type TokenVerifier interface { VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken string) (*auth.Token, error) } // AuthMiddleware verifies the Bearer token and injects uid + claims into the // request context via httpauth.SetTokenData. Requests to publicPaths are skipped // without token verification (wildcards supported via path.Match). // Returns 401 on missing or invalid tokens. func AuthMiddleware(verifier TokenVerifier, publicPaths []string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { for _, pattern := range publicPaths { if matched, _ := path.Match(pattern, r.URL.Path); matched { next.ServeHTTP(w, r) return } } authHeader := r.Header.Get("Authorization") if !strings.HasPrefix(authHeader, "Bearer ") { http.Error(w, "unauthorized", http.StatusUnauthorized) return } token := strings.TrimPrefix(authHeader, "Bearer ") decoded, err := verifier.VerifyIDTokenAndCheckRevoked(r.Context(), token) if err != nil { http.Error(w, "unauthorized", http.StatusUnauthorized) return } ctx := httpauthmw.SetTokenData(r.Context(), decoded.UID, decoded.Claims) next.ServeHTTP(w, r.WithContext(ctx)) }) } }