package auth import ( "context" "net/http" "strings" "github.com/wangjia/pangolin/server/internal/codes" ) // ctxKey is this package's private context-key type for values other than the // numeric user id (which is shared with the codes module — see below). type ctxKey string const ( // ctxKeyUserUUID stores the authenticated user's UUID (the JWT subject). ctxKeyUserUUID ctxKey = "user_uuid" // ctxKeyClaims stores the full *Claims for handlers that need jti/exp. ctxKeyClaims ctxKey = "claims" ) // The numeric user id is injected under codes.CtxKeyUserID so that the codes // module (and any other module sharing that exported key) reads the same value // without an import cycle — codes deliberately exports the key for this purpose. // RequireAuth returns middleware that enforces a valid Bearer access token on // every wrapped route. On success it injects the numeric user id, the user // UUID, and the parsed claims into the request context. This is the auth base // for all protected /v1 routes (everything except the auth group). func RequireAuth(tm *TokenManager) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { token, ok := bearerToken(r) if !ok { writeAPIErr(w, ErrUnauthorized, 0) return } claims, err := tm.ParseAccess(token) if err != nil { writeAPIErr(w, ErrUnauthorized, 0) return } ctx := r.Context() ctx = context.WithValue(ctx, codes.CtxKeyUserID, claims.UID) ctx = context.WithValue(ctx, ctxKeyUserUUID, claims.Subject) ctx = context.WithValue(ctx, ctxKeyClaims, claims) next.ServeHTTP(w, r.WithContext(ctx)) }) } } // bearerToken extracts the token from an "Authorization: Bearer " header. func bearerToken(r *http.Request) (string, bool) { h := r.Header.Get("Authorization") if h == "" { return "", false } const prefix = "Bearer " if len(h) <= len(prefix) || !strings.EqualFold(h[:len(prefix)], prefix) { return "", false } token := strings.TrimSpace(h[len(prefix):]) if token == "" { return "", false } return token, true } // UserIDFromContext returns the numeric user id injected by RequireAuth. func UserIDFromContext(ctx context.Context) (int64, bool) { v, ok := ctx.Value(codes.CtxKeyUserID).(int64) return v, ok && v != 0 } // UserUUIDFromContext returns the user UUID injected by RequireAuth. func UserUUIDFromContext(ctx context.Context) (string, bool) { v, ok := ctx.Value(ctxKeyUserUUID).(string) return v, ok && v != "" } // ClaimsFromContext returns the parsed access-token claims injected by RequireAuth. func ClaimsFromContext(ctx context.Context) (*Claims, bool) { v, ok := ctx.Value(ctxKeyClaims).(*Claims) return v, ok }