package infra import ( "context" "database/sql" "net/http" "strings" ) // SessionCookieConfig configura el middleware de autenticacion por cookie/Bearer. type SessionCookieConfig struct { DB *sql.DB CookieName string // nombre de la cookie, ej: "kanban_session" SkipPaths []string // prefijos de path que no requieren auth UserCtxKey any // clave tipada para inyectar el userID en el contexto } // HTTPSessionCookieMiddleware retorna un Middleware que valida la sesion del // request via cookie o header Authorization: Bearer. // Si el path esta en SkipPaths delega sin validar. // Si el token es valido inyecta el userID en r.Context() con cfg.UserCtxKey. // Responde 401 JSON si falta el token o la sesion es invalida. func HTTPSessionCookieMiddleware(cfg SessionCookieConfig) Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 1. Skip paths for _, prefix := range cfg.SkipPaths { if strings.HasPrefix(r.URL.Path, prefix) { next.ServeHTTP(w, r) return } } // 2. Extraer token: primero cookie, luego Authorization header token := "" if c, err := r.Cookie(cfg.CookieName); err == nil { token = c.Value } else { auth := r.Header.Get("Authorization") if strings.HasPrefix(auth, "Bearer ") { token = strings.TrimPrefix(auth, "Bearer ") } } // 3. Sin token → 401 if token == "" { HTTPErrorResponse(w, HTTPError{ Status: http.StatusUnauthorized, Code: "unauthorized", Message: "session required", }) return } // 4. Validar sesion session, err := SessionValidate(cfg.DB, token) if err != nil { HTTPErrorResponse(w, HTTPError{ Status: http.StatusUnauthorized, Code: "invalid_session", Message: err.Error(), }) return } // 5. Inyectar userID en contexto y delegar ctx := context.WithValue(r.Context(), cfg.UserCtxKey, session.UserID) next.ServeHTTP(w, r.WithContext(ctx)) }) } } // UserIDFromContext extrae el userID del contexto usando la clave tipada dada. // Retorna ("", false) si no esta presente o el tipo no coincide. func UserIDFromContext(ctx context.Context, key any) (string, bool) { v, ok := ctx.Value(key).(string) return v, ok }