package auth import ( "fmt" "net/http" "net/http/httptest" "testing" "time" "github.com/alicebob/miniredis/v2" "github.com/gin-gonic/gin" goredis "github.com/redis/go-redis/v9" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func init() { gin.SetMode(gin.TestMode) } func setupTestMiddleware(t *testing.T) (*Manager, *SessionStore, *miniredis.Miniredis) { t.Helper() mr := miniredis.RunT(t) rdb := goredis.NewClient(&goredis.Options{Addr: mr.Addr()}) mgr := NewManager("test-middleware-secret", 15*time.Minute, 7*24*time.Hour) sessions := NewSessionStore(rdb) return mgr, sessions, mr } func TestAuthMiddleware_ValidToken(t *testing.T) { mgr, sessions, _ := setupTestMiddleware(t) pair, err := mgr.Issue(1, 10, 100, "editor") require.NoError(t, err) r := gin.New() r.Use(Middleware(mgr, sessions)) var gotActor Actor r.GET("/test", func(c *gin.Context) { a, ok := ActorFromCtx(c.Request.Context()) assert.True(t, ok) gotActor = a c.Status(http.StatusOK) }) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/test", nil) req.Header.Set("Authorization", "Bearer "+pair.AccessToken) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, int64(1), gotActor.UserID) assert.Equal(t, int64(10), gotActor.TenantID) assert.Equal(t, int64(100), gotActor.PrimaryWorkspaceID) assert.Equal(t, "editor", gotActor.Role) } func TestAuthMiddleware_MissingHeader(t *testing.T) { mgr, sessions, _ := setupTestMiddleware(t) r := gin.New() r.Use(Middleware(mgr, sessions)) r.GET("/test", func(c *gin.Context) { c.Status(http.StatusOK) }) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/test", nil) r.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) } func TestAuthMiddleware_InvalidToken(t *testing.T) { mgr, sessions, _ := setupTestMiddleware(t) r := gin.New() r.Use(Middleware(mgr, sessions)) r.GET("/test", func(c *gin.Context) { c.Status(http.StatusOK) }) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/test", nil) req.Header.Set("Authorization", "Bearer bad-token-value") r.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) } func TestAuthMiddleware_RefreshTokenRejected(t *testing.T) { mgr, sessions, _ := setupTestMiddleware(t) pair, err := mgr.Issue(1, 10, 100, "editor") require.NoError(t, err) r := gin.New() r.Use(Middleware(mgr, sessions)) r.GET("/test", func(c *gin.Context) { c.Status(http.StatusOK) }) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/test", nil) req.Header.Set("Authorization", "Bearer "+pair.RefreshToken) r.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) } func TestAuthMiddleware_BlacklistedToken(t *testing.T) { mgr, sessions, mr := setupTestMiddleware(t) pair, err := mgr.Issue(1, 10, 100, "editor") require.NoError(t, err) // Blacklist the access token's JTI require.NoError(t, mr.Set(fmt.Sprintf("blacklist:%s", pair.AccessJTI), "1")) r := gin.New() r.Use(Middleware(mgr, sessions)) r.GET("/test", func(c *gin.Context) { c.Status(http.StatusOK) }) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/test", nil) req.Header.Set("Authorization", "Bearer "+pair.AccessToken) r.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) }