This commit is contained in:
Krispire Admin
2026-05-30 02:33:57 +05:30
parent 5e0a484239
commit 97422c1ef5
21 changed files with 1720 additions and 82 deletions
+48 -26
View File
@@ -2,56 +2,78 @@ package middleware
import (
"context"
"errors"
"net/http"
"strings"
"github.com/prabhupadasays/spsays-bff/internal/repository"
"github.com/prabhupadasays/spsays-bff/pkg/auth"
"github.com/prabhupadasays/spsays-bff/pkg/response"
)
type contextKey string
type ctxKey string
const UserIDKey contextKey = "user_id"
const (
userIDKey ctxKey = "user_id"
tokenVersionKey ctxKey = "tv"
)
// Auth is a placeholder middleware for JWT authentication.
// It extracts the Bearer token from the Authorization header,
// validates it, and sets the user ID in the request context.
func Auth(jwtSecret string) func(next http.Handler) http.Handler {
// RequireAuth is the middleware applied to every protected route.
//
// It pulls the JWT from the cookie, verifies signature + expiry, then asks
// the UserRepo for the current token_version and rejects if the JWT carries
// a stale version (password-change or forced logout invalidates older JWTs).
//
// On success it stuffs (user_id, token_version) into the request context;
// handlers read them via UserID() / TokenVersion().
func RequireAuth(issuer *auth.Issuer, users *repository.UserRepo) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
response.Error(w, http.StatusUnauthorized, "missing authorization header")
raw := auth.Read(r)
if raw == "" {
response.Error(w, http.StatusUnauthorized, "not authenticated")
return
}
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" {
response.Error(w, http.StatusUnauthorized, "invalid authorization format")
claims, err := issuer.Parse(raw)
if err != nil {
response.Error(w, http.StatusUnauthorized, "invalid or expired session")
return
}
token := parts[1]
// TODO: validate JWT token and extract user ID
_ = token
_ = jwtSecret
userID := "" // placeholder
if userID == "" {
response.Error(w, http.StatusUnauthorized, "invalid or expired token")
u, err := users.FindByID(r.Context(), claims.UserID)
if err != nil {
if errors.Is(err, repository.ErrUserNotFound) {
response.Error(w, http.StatusUnauthorized, "account no longer exists")
return
}
response.Error(w, http.StatusInternalServerError, "auth check failed")
return
}
if u.TokenVersion != claims.TokenVersion {
response.Error(w, http.StatusUnauthorized, "session expired — please log in again")
return
}
ctx := context.WithValue(r.Context(), UserIDKey, userID)
ctx := context.WithValue(r.Context(), userIDKey, claims.UserID)
ctx = context.WithValue(ctx, tokenVersionKey, claims.TokenVersion)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// GetUserID extracts the user ID from the request context.
func GetUserID(ctx context.Context) string {
if v, ok := ctx.Value(UserIDKey).(string); ok {
// UserID returns the authenticated user's id from the request context.
// Returns "" if RequireAuth has not run.
func UserID(ctx context.Context) string {
if v, ok := ctx.Value(userIDKey).(string); ok {
return v
}
return ""
}
// TokenVersion returns the JWT's token_version claim from context.
func TokenVersion(ctx context.Context) int {
if v, ok := ctx.Value(tokenVersionKey).(int); ok {
return v
}
return 0
}