package auth import ( "context" "net/http" "github.com/google/uuid" ) type contextKey string const ( userIDContextKey contextKey = "paliad.userID" claimsContextKey contextKey = "paliad.claims" ) // UserIDFromContext returns the authenticated user's UUID, populated by // WithUserID middleware (which runs after the session middleware). // Returns (uuid.Nil, false) if no user is in context. func UserIDFromContext(ctx context.Context) (uuid.UUID, bool) { v, ok := ctx.Value(userIDContextKey).(uuid.UUID) if !ok { return uuid.Nil, false } return v, true } // withVerifiedClaims stores signature-verified JWT claims in the request // context. Called only from Client.Middleware after VerifyToken succeeds. func withVerifiedClaims(ctx context.Context, claims *VerifiedClaims) context.Context { return context.WithValue(ctx, claimsContextKey, claims) } // verifiedClaimsFromContext returns the signature-verified JWT claims // attached by Client.Middleware. func verifiedClaimsFromContext(ctx context.Context) (*VerifiedClaims, bool) { v, ok := ctx.Value(claimsContextKey).(*VerifiedClaims) return v, ok } // ClaimsFromContext is the public accessor for the verified JWT claims // attached by Client.Middleware. Handlers that need the raw email claim // (onboarding uses it to seed paliad.users.email) go through this. func ClaimsFromContext(ctx context.Context) (*VerifiedClaims, bool) { return verifiedClaimsFromContext(ctx) } // WithUserID reads the `sub` claim from verified JWT claims attached by // Client.Middleware and injects the user's UUID into the request context. // Must run after Client.Middleware — the claims are only set there, after // signature verification succeeds. func (c *Client) WithUserID(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { claims, ok := verifiedClaimsFromContext(r.Context()) if !ok { next.ServeHTTP(w, r) return } uid, err := uuid.Parse(claims.Sub) if err != nil { next.ServeHTTP(w, r) return } ctx := context.WithValue(r.Context(), userIDContextKey, uid) next.ServeHTTP(w, r.WithContext(ctx)) }) }