package middleware import ( "context" "fmt" "log" "net/http" "strings" "gitea.local/admin/hspguard/internal/config" "gitea.local/admin/hspguard/internal/repository" "gitea.local/admin/hspguard/internal/types" "gitea.local/admin/hspguard/internal/util" "gitea.local/admin/hspguard/internal/web" "github.com/google/uuid" ) type AuthMiddleware struct { cfg *config.AppConfig repo *repository.Queries } func NewAuthMiddleware(cfg *config.AppConfig, repo *repository.Queries) *AuthMiddleware { return &AuthMiddleware{ cfg, repo, } } func (m *AuthMiddleware) Runner(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { authHeader := r.Header.Get("Authorization") if authHeader == "" { web.Error(w, "unauthorized", http.StatusUnauthorized) return } parts := strings.Split(authHeader, "Bearer ") if len(parts) != 2 { web.Error(w, "invalid auth header format", http.StatusUnauthorized) return } tokenStr := parts[1] var userClaims types.UserClaims token, err := util.VerifyToken(tokenStr, m.cfg.Jwt.PublicKey, &userClaims) if err != nil || !token.Valid { web.Error(w, fmt.Sprintf("invalid token: %v", err), http.StatusUnauthorized) return } // TODO: redis caching parsed, err := uuid.Parse(userClaims.ID) if err != nil { log.Printf("ERR: Failed to parse token JTI '%s': %v\n", userClaims.ID, err) web.Error(w, "failed to get session", http.StatusUnauthorized) return } session, err := m.repo.GetUserSessionByAccessJTI(r.Context(), &parsed) if err != nil { log.Printf("ERR: Failed to find session with '%s' JTI: %v\n", parsed.String(), err) web.Error(w, "no session found", http.StatusUnauthorized) return } if !session.IsActive { log.Printf("INFO: Inactive session trying to authorize: %s\n", session.AccessTokenID) web.Error(w, "no session found", http.StatusUnauthorized) return } ctx := context.WithValue(r.Context(), types.UserIdKey, userClaims.Subject) ctx = context.WithValue(ctx, types.JTIKey, userClaims.ID) next.ServeHTTP(w, r.WithContext(ctx)) }) } func WithSkipper(mw func(http.Handler) http.Handler, excludedPaths ...string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { for _, path := range excludedPaths { if strings.HasPrefix(r.URL.Path, path) { next.ServeHTTP(w, r) return } } mw(next).ServeHTTP(w, r) }) } }