diff --git a/openapi/oauth/core_test.go b/openapi/oauth/core_test.go index 6edd15ca..3db677d3 100644 --- a/openapi/oauth/core_test.go +++ b/openapi/oauth/core_test.go @@ -400,9 +400,6 @@ func TestRefreshToken(t *testing.T) { assert.NotEmpty(t, response.RefreshToken) assert.NotEqual(t, refreshToken, response.RefreshToken) // Should be different - // Old refresh token should be revoked - exists := service.userProvider.TokenExists(refreshToken) - assert.False(t, exists) }) t.Run("invalid refresh token", func(t *testing.T) { @@ -499,9 +496,6 @@ func TestRotateRefreshToken(t *testing.T) { assert.Equal(t, "Bearer", response.TokenType) assert.Equal(t, 3600, response.ExpiresIn) - // Old token should be revoked - exists := service.userProvider.TokenExists(oldToken) - assert.False(t, exists) }) t.Run("rotation with disabled feature", func(t *testing.T) { @@ -661,9 +655,6 @@ func TestHandleRefreshTokenGrant(t *testing.T) { assert.NotEmpty(t, token.RefreshToken) assert.NotEqual(t, refreshToken, token.RefreshToken) // Should be different - // Old refresh token should be revoked - exists := service.userProvider.TokenExists(refreshToken) - assert.False(t, exists) }) t.Run("refresh token grant without rotation", func(t *testing.T) { @@ -693,9 +684,6 @@ func TestHandleRefreshTokenGrant(t *testing.T) { assert.Equal(t, 3600, token.ExpiresIn) assert.Equal(t, refreshToken, token.RefreshToken) // Should be the same - // Old refresh token should still exist - exists := service.userProvider.TokenExists(refreshToken) - assert.True(t, exists) }) t.Run("refresh token grant with invalid token", func(t *testing.T) { diff --git a/openapi/oauth/providers/user/default.go b/openapi/oauth/providers/user/default.go index 90c9c346..6dbe3156 100644 --- a/openapi/oauth/providers/user/default.go +++ b/openapi/oauth/providers/user/default.go @@ -284,67 +284,67 @@ func (u *DefaultUser) ValidateUserScope(ctx context.Context, userID string, scop return true, nil } -// StoreToken stores a token in the token store with expiration time -func (u *DefaultUser) StoreToken(accessToken string, tokenData map[string]interface{}, expiration time.Duration) error { - return u.tokenStore.Set(u.tokenKey(accessToken), tokenData, expiration) -} +// // StoreToken stores a token in the token store with expiration time +// func (u *DefaultUser) StoreToken(accessToken string, tokenData map[string]interface{}, expiration time.Duration) error { +// return u.tokenStore.Set(u.tokenKey(accessToken), tokenData, expiration) +// } -// RevokeToken revokes a token by removing it from the token store -func (u *DefaultUser) RevokeToken(accessToken string) error { - u.tokenStore.Del(u.tokenKey(accessToken)) - return nil -} +// // RevokeToken revokes a token by removing it from the token store +// func (u *DefaultUser) RevokeToken(accessToken string) error { +// u.tokenStore.Del(u.tokenKey(accessToken)) +// return nil +// } -// TokenExists checks if a token exists in the token store -func (u *DefaultUser) TokenExists(accessToken string) bool { - _, exists := u.tokenStore.Get(u.tokenKey(accessToken)) - return exists -} +// // TokenExists checks if a token exists in the token store +// func (u *DefaultUser) TokenExists(accessToken string) bool { +// _, exists := u.tokenStore.Get(u.tokenKey(accessToken)) +// return exists +// } -// GetTokenData retrieves token data from the token store -func (u *DefaultUser) GetTokenData(accessToken string) (map[string]interface{}, error) { - tokenData, exists := u.tokenStore.Get(u.tokenKey(accessToken)) - if !exists { - return nil, fmt.Errorf("token not found") - } +// // GetTokenData retrieves token data from the token store +// func (u *DefaultUser) GetTokenData(accessToken string) (map[string]interface{}, error) { +// tokenData, exists := u.tokenStore.Get(u.tokenKey(accessToken)) +// if !exists { +// return nil, fmt.Errorf("token not found") +// } - // Try to convert to map[string]interface{} directly - if tokenInfo, ok := tokenData.(map[string]interface{}); ok { - return tokenInfo, nil - } +// // Try to convert to map[string]interface{} directly +// if tokenInfo, ok := tokenData.(map[string]interface{}); ok { +// return tokenInfo, nil +// } - // If direct conversion fails, try to handle other possible types - // This handles cases where MongoDB might return different types - switch v := tokenData.(type) { - case map[string]interface{}: - return v, nil - case map[interface{}]interface{}: - // Convert map[interface{}]interface{} to map[string]interface{} - result := make(map[string]interface{}) - for key, val := range v { - if keyStr, ok := key.(string); ok { - result[keyStr] = val - } - } - return result, nil - default: - // Try to convert using map[string]interface{} casting - // This handles primitive.M and other MongoDB types - if reflect.TypeOf(v).Kind() == reflect.Map { - result := make(map[string]interface{}) - rv := reflect.ValueOf(v) - for _, key := range rv.MapKeys() { - if keyStr, ok := key.Interface().(string); ok { - result[keyStr] = rv.MapIndex(key).Interface() - } - } - if len(result) > 0 { - return result, nil - } - } - return nil, fmt.Errorf("invalid token data format: %T", tokenData) - } -} +// // If direct conversion fails, try to handle other possible types +// // This handles cases where MongoDB might return different types +// switch v := tokenData.(type) { +// case map[string]interface{}: +// return v, nil +// case map[interface{}]interface{}: +// // Convert map[interface{}]interface{} to map[string]interface{} +// result := make(map[string]interface{}) +// for key, val := range v { +// if keyStr, ok := key.(string); ok { +// result[keyStr] = val +// } +// } +// return result, nil +// default: +// // Try to convert using map[string]interface{} casting +// // This handles primitive.M and other MongoDB types +// if reflect.TypeOf(v).Kind() == reflect.Map { +// result := make(map[string]interface{}) +// rv := reflect.ValueOf(v) +// for _, key := range rv.MapKeys() { +// if keyStr, ok := key.Interface().(string); ok { +// result[keyStr] = rv.MapIndex(key).Interface() +// } +// } +// if len(result) > 0 { +// return result, nil +// } +// } +// return nil, fmt.Errorf("invalid token data format: %T", tokenData) +// } +// } // CreateUser creates a new user in the database func (u *DefaultUser) CreateUser(userData map[string]interface{}) (interface{}, error) { diff --git a/openapi/oauth/providers/user/default_test.go b/openapi/oauth/providers/user/default_test.go index a9f1c62e..28dfaf03 100644 --- a/openapi/oauth/providers/user/default_test.go +++ b/openapi/oauth/providers/user/default_test.go @@ -350,64 +350,6 @@ func TestKeyGeneration(t *testing.T) { } } -func TestTokenOperations(t *testing.T) { - storeConfigs := getStoreConfigs() - - for _, config := range storeConfigs { - t.Run(config.Name, func(t *testing.T) { - tokenStore := config.GetFunc(t) - cache := getLRUCache(t) - - user := NewDefaultUser(&DefaultUserOptions{ - Prefix: "test:", - - Cache: cache, - TokenStore: tokenStore, - }) - - // Clean up - tokenStore.Clear() - - t.Run("store and get token", func(t *testing.T) { - tokenData := createTestToken("test-subject") - err := user.StoreToken("test-token", tokenData, 1*time.Hour) - assert.NoError(t, err) - - exists := user.TokenExists("test-token") - assert.True(t, exists) - - retrievedData, err := user.GetTokenData("test-token") - assert.NoError(t, err) - assert.Equal(t, tokenData["subject"], retrievedData["subject"]) - }) - - t.Run("revoke token", func(t *testing.T) { - tokenData := createTestToken("test-subject") - err := user.StoreToken("test-token-revoke", tokenData, 1*time.Hour) - assert.NoError(t, err) - - exists := user.TokenExists("test-token-revoke") - assert.True(t, exists) - - err = user.RevokeToken("test-token-revoke") - assert.NoError(t, err) - - exists = user.TokenExists("test-token-revoke") - assert.False(t, exists) - }) - - t.Run("non-existent token", func(t *testing.T) { - exists := user.TokenExists("non-existent") - assert.False(t, exists) - - _, err := user.GetTokenData("non-existent") - assert.Error(t, err) - assert.Contains(t, err.Error(), "token not found") - }) - }) - } -} - func TestGetUserBySubject(t *testing.T) { storeConfigs := getStoreConfigs() @@ -557,70 +499,6 @@ func TestGetUserByEmail(t *testing.T) { } } -func TestGetUserByAccessToken(t *testing.T) { - storeConfigs := getStoreConfigs() - - for _, config := range storeConfigs { - t.Run(config.Name, func(t *testing.T) { - cleanupTestData(t) - defer cleanupTestData(t) - - tokenStore := config.GetFunc(t) - cache := getLRUCache(t) - - user := NewDefaultUser(&DefaultUserOptions{ - Prefix: "test:", - - Cache: cache, - TokenStore: tokenStore, - }) - - // Clean up - tokenStore.Clear() - - // Create test user - testUser := createTestUser("token1") - setupTestUser(t, testUser) - - ctx := context.Background() - - t.Run("get user by access token", func(t *testing.T) { - // Store token - tokenData := createTestToken(testUser.Subject) - err := user.StoreToken("test-access-token", tokenData, 1*time.Hour) - require.NoError(t, err) - - // Get user by token - retrievedUser, err := user.GetUserByAccessToken(ctx, "test-access-token") - assert.NoError(t, err) - assert.NotNil(t, retrievedUser) - - userMap := convertToStringMap(t, retrievedUser) - assert.Equal(t, testUser.Subject, userMap["subject"]) - assert.Equal(t, testUser.Username, userMap["username"]) - }) - - t.Run("non-existent token", func(t *testing.T) { - retrievedUser, err := user.GetUserByAccessToken(ctx, "non-existent-token") - assert.Error(t, err) - assert.Nil(t, retrievedUser) - assert.Contains(t, err.Error(), "token not found") - }) - - t.Run("invalid token format", func(t *testing.T) { - // Store invalid token data - invalidTokenData := "invalid-token-data" - tokenStore.Set(user.tokenKey("invalid-token"), invalidTokenData, 1*time.Hour) - - retrievedUser, err := user.GetUserByAccessToken(ctx, "invalid-token") - assert.Error(t, err) - assert.Nil(t, retrievedUser) - assert.Contains(t, err.Error(), "invalid token data format") - }) - }) - } -} - func TestValidateUserScope(t *testing.T) { storeConfigs := getStoreConfigs() diff --git a/openapi/oauth/types/interfaces.go b/openapi/oauth/types/interfaces.go index c53bd7b3..2677c615 100644 --- a/openapi/oauth/types/interfaces.go +++ b/openapi/oauth/types/interfaces.go @@ -2,7 +2,6 @@ package types import ( "context" - "time" "github.com/gin-gonic/gin" ) @@ -154,16 +153,16 @@ type UserProvider interface { // Token management methods // StoreToken stores a token with expiration time - StoreToken(accessToken string, tokenData map[string]interface{}, expiration time.Duration) error + // StoreToken(accessToken string, tokenData map[string]interface{}, expiration time.Duration) error // RevokeToken revokes a token by removing it from storage - RevokeToken(accessToken string) error + // RevokeToken(accessToken string) error // TokenExists checks if a token exists in storage - TokenExists(accessToken string) bool + // TokenExists(accessToken string) bool // GetTokenData retrieves token data from storage - GetTokenData(accessToken string) (map[string]interface{}, error) + // GetTokenData(accessToken string) (map[string]interface{}, error) // User management methods // CreateUser creates a new user in the database diff --git a/openapi/oauth/user_test.go b/openapi/oauth/user_test.go index 299f1145..c90af7b3 100644 --- a/openapi/oauth/user_test.go +++ b/openapi/oauth/user_test.go @@ -1,439 +1 @@ package oauth - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// ============================================================================= -// UserInfo Tests -// ============================================================================= - -func TestUserInfo(t *testing.T) { - service, _, _, cleanup := setupOAuthTestEnvironment(t) - defer cleanup() - - ctx := context.Background() - userProvider := service.GetUserProvider() - - t.Run("get user info with valid access token", func(t *testing.T) { - // Create a valid access token for the first test user - testUser := testUsers[0] - accessToken := "valid_access_token_123" - - // Store token data in user provider - tokenData := map[string]interface{}{ - "token": accessToken, - "user_id": testUser.ID, - "subject": testUser.Subject, - "username": testUser.Username, - "email": testUser.Email, - "first_name": testUser.FirstName, - "last_name": testUser.LastName, - "full_name": testUser.FullName, - "scopes": testUser.Scopes, - "status": testUser.Status, - "exp": time.Now().Add(time.Hour).Unix(), - "iat": time.Now().Unix(), - "token_type": "Bearer", - } - - // Store the token data - err := userProvider.StoreToken(accessToken, tokenData, time.Hour) - require.NoError(t, err) - - // Get user info using the access token - userInfo, err := service.UserInfo(ctx, accessToken) - assert.NoError(t, err) - assert.NotNil(t, userInfo) - - // Verify the user info contains expected data - if userInfoMap, ok := userInfo.(map[string]interface{}); ok { - assert.Equal(t, testUser.Subject, userInfoMap["subject"]) - assert.Equal(t, testUser.Username, userInfoMap["username"]) - assert.Equal(t, testUser.Email, userInfoMap["email"]) - } - }) - - t.Run("get user info with invalid access token", func(t *testing.T) { - invalidToken := "invalid_access_token_xyz" - - userInfo, err := service.UserInfo(ctx, invalidToken) - assert.Error(t, err) - assert.Nil(t, userInfo) - }) - - t.Run("get user info with non-existent access token", func(t *testing.T) { - nonExistentToken := "non_existent_token_abc" - - userInfo, err := service.UserInfo(ctx, nonExistentToken) - assert.Error(t, err) - assert.Nil(t, userInfo) - }) - - t.Run("get user info with empty access token", func(t *testing.T) { - emptyToken := "" - - userInfo, err := service.UserInfo(ctx, emptyToken) - assert.Error(t, err) - assert.Nil(t, userInfo) - }) - - t.Run("get user info with expired access token", func(t *testing.T) { - testUser := testUsers[1] - expiredToken := "expired_access_token_456" - - // Store expired token data - tokenData := map[string]interface{}{ - "token": expiredToken, - "user_id": testUser.ID, - "subject": testUser.Subject, - "username": testUser.Username, - "email": testUser.Email, - "scopes": testUser.Scopes, - "status": testUser.Status, - "exp": time.Now().Add(-time.Hour).Unix(), // Expired 1 hour ago - "iat": time.Now().Add(-2 * time.Hour).Unix(), - "token_type": "Bearer", - } - - err := userProvider.StoreToken(expiredToken, tokenData, time.Hour) - require.NoError(t, err) - - userInfo, err := service.UserInfo(ctx, expiredToken) - // UserInfo method returns user data regardless of token expiry - assert.NoError(t, err) - assert.NotNil(t, userInfo) - - // Verify user info contains expected data - if userInfoMap, ok := userInfo.(map[string]interface{}); ok { - assert.Equal(t, testUser.Subject, userInfoMap["subject"]) - assert.Equal(t, testUser.Username, userInfoMap["username"]) - } - }) - - t.Run("get user info with inactive user", func(t *testing.T) { - // Use the inactive test user - inactiveUser := testUsers[4] // inactive.user - inactiveToken := "inactive_user_token_789" - - tokenData := map[string]interface{}{ - "token": inactiveToken, - "user_id": inactiveUser.ID, - "subject": inactiveUser.Subject, - "username": inactiveUser.Username, - "email": inactiveUser.Email, - "scopes": inactiveUser.Scopes, - "status": inactiveUser.Status, // inactive - "exp": time.Now().Add(time.Hour).Unix(), - "iat": time.Now().Unix(), - "token_type": "Bearer", - } - - err := userProvider.StoreToken(inactiveToken, tokenData, time.Hour) - require.NoError(t, err) - - userInfo, err := service.UserInfo(ctx, inactiveToken) - // UserInfo method returns user data regardless of user status - assert.NoError(t, err) - assert.NotNil(t, userInfo) - - // Verify user info contains expected data - if userInfoMap, ok := userInfo.(map[string]interface{}); ok { - assert.Equal(t, inactiveUser.Subject, userInfoMap["subject"]) - assert.Equal(t, inactiveUser.Username, userInfoMap["username"]) - assert.Equal(t, inactiveUser.Status, userInfoMap["status"]) - } - }) - - t.Run("get user info with limited scope user", func(t *testing.T) { - // Use the limited scope test user - limitedUser := testUsers[5] // limited.user - limitedToken := "limited_scope_token_101" - - tokenData := map[string]interface{}{ - "token": limitedToken, - "user_id": limitedUser.ID, - "subject": limitedUser.Subject, - "username": limitedUser.Username, - "email": limitedUser.Email, - "scopes": limitedUser.Scopes, // Only openid - "status": limitedUser.Status, - "exp": time.Now().Add(time.Hour).Unix(), - "iat": time.Now().Unix(), - "token_type": "Bearer", - } - - err := userProvider.StoreToken(limitedToken, tokenData, time.Hour) - require.NoError(t, err) - - userInfo, err := service.UserInfo(ctx, limitedToken) - assert.NoError(t, err) - assert.NotNil(t, userInfo) - - // Verify limited user info - if userInfoMap, ok := userInfo.(map[string]interface{}); ok { - assert.Equal(t, limitedUser.Subject, userInfoMap["subject"]) - assert.Equal(t, limitedUser.Username, userInfoMap["username"]) - // Should only have basic scopes - if scopes, ok := userInfoMap["scopes"].([]string); ok { - assert.Contains(t, scopes, "openid") - assert.Len(t, scopes, 1) - } - } - }) - - t.Run("get user info with admin user", func(t *testing.T) { - // Use the admin test user - adminUser := testUsers[0] // admin - adminToken := "admin_token_202" - - tokenData := map[string]interface{}{ - "token": adminToken, - "user_id": adminUser.ID, - "subject": adminUser.Subject, - "username": adminUser.Username, - "email": adminUser.Email, - "first_name": adminUser.FirstName, - "last_name": adminUser.LastName, - "full_name": adminUser.FullName, - "scopes": adminUser.Scopes, - "status": adminUser.Status, - "email_verified": adminUser.EmailVerified, - "mobile_verified": adminUser.MobileVerified, - "two_factor_enabled": adminUser.TwoFactorEnabled, - "exp": time.Now().Add(time.Hour).Unix(), - "iat": time.Now().Unix(), - "token_type": "Bearer", - } - - err := userProvider.StoreToken(adminToken, tokenData, time.Hour) - require.NoError(t, err) - - userInfo, err := service.UserInfo(ctx, adminToken) - assert.NoError(t, err) - assert.NotNil(t, userInfo) - - // Verify admin user info - if userInfoMap, ok := userInfo.(map[string]interface{}); ok { - assert.Equal(t, adminUser.Subject, userInfoMap["subject"]) - assert.Equal(t, adminUser.Username, userInfoMap["username"]) - assert.Equal(t, adminUser.Email, userInfoMap["email"]) - assert.True(t, userInfoMap["email_verified"].(bool)) - assert.True(t, userInfoMap["two_factor_enabled"].(bool)) - - // Should have admin scopes - if scopes, ok := userInfoMap["scopes"].([]string); ok { - assert.Contains(t, scopes, "admin") - assert.Contains(t, scopes, "openid") - assert.Contains(t, scopes, "profile") - assert.Contains(t, scopes, "email") - } - } - }) -} - -// ============================================================================= -// Integration Tests -// ============================================================================= - -func TestUserInfoIntegration(t *testing.T) { - service, _, _, cleanup := setupOAuthTestEnvironment(t) - defer cleanup() - - ctx := context.Background() - userProvider := service.GetUserProvider() - - t.Run("complete user info flow", func(t *testing.T) { - // Use different test users for comprehensive testing - testCases := []struct { - name string - user *TestUser - tokenSuffix string - }{ - {"regular_user", testUsers[1], "regular"}, - {"verified_user", testUsers[2], "verified"}, - {"secure_user", testUsers[6], "secure"}, - {"api_user", testUsers[7], "api"}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - token := "integration_token_" + tc.tokenSuffix - - tokenData := map[string]interface{}{ - "token": token, - "user_id": tc.user.ID, - "subject": tc.user.Subject, - "username": tc.user.Username, - "email": tc.user.Email, - "scopes": tc.user.Scopes, - "status": tc.user.Status, - "exp": time.Now().Add(time.Hour).Unix(), - "iat": time.Now().Unix(), - "token_type": "Bearer", - } - - err := userProvider.StoreToken(token, tokenData, time.Hour) - require.NoError(t, err) - - userInfo, err := service.UserInfo(ctx, token) - assert.NoError(t, err) - assert.NotNil(t, userInfo) - - // Verify basic user info structure - if userInfoMap, ok := userInfo.(map[string]interface{}); ok { - assert.Equal(t, tc.user.Subject, userInfoMap["subject"]) - assert.Equal(t, tc.user.Username, userInfoMap["username"]) - assert.Equal(t, tc.user.Email, userInfoMap["email"]) - assert.Equal(t, tc.user.Status, userInfoMap["status"]) - } - }) - } - }) - - t.Run("concurrent user info requests", func(t *testing.T) { - // Test concurrent access to user info - const numRequests = 10 - - // Create tokens for concurrent testing - tokens := make([]string, numRequests) - for i := 0; i < numRequests; i++ { - tokens[i] = fmt.Sprintf("concurrent_token_%d", i) - testUser := testUsers[i%len(testUsers)] - - tokenData := map[string]interface{}{ - "token": tokens[i], - "user_id": testUser.ID, - "subject": testUser.Subject, - "username": testUser.Username, - "email": testUser.Email, - "scopes": testUser.Scopes, - "status": testUser.Status, - "exp": time.Now().Add(time.Hour).Unix(), - "iat": time.Now().Unix(), - "token_type": "Bearer", - } - - err := userProvider.StoreToken(tokens[i], tokenData, time.Hour) - require.NoError(t, err) - } - - // Make concurrent requests - results := make(chan error, numRequests) - for i := 0; i < numRequests; i++ { - go func(token string) { - userInfo, err := service.UserInfo(ctx, token) - if err != nil { - results <- err - return - } - if userInfo == nil { - results <- fmt.Errorf("user info is nil") - return - } - results <- nil - }(tokens[i]) - } - - // Collect results - for i := 0; i < numRequests; i++ { - err := <-results - assert.NoError(t, err) - } - }) -} - -// ============================================================================= -// Edge Cases and Error Handling -// ============================================================================= - -func TestUserInfoEdgeCases(t *testing.T) { - service, _, _, cleanup := setupOAuthTestEnvironment(t) - defer cleanup() - - ctx := context.Background() - userProvider := service.GetUserProvider() - - t.Run("malformed token data", func(t *testing.T) { - malformedToken := "malformed_token_data" - - // Store malformed token data - tokenData := map[string]interface{}{ - "token": malformedToken, - "user_id": "invalid_user_id", - "subject": nil, // Invalid subject - "username": "", // Empty username - "exp": "not_a_number", // Invalid expiration - "iat": time.Now().Unix(), - "token_type": "Bearer", - } - - err := userProvider.StoreToken(malformedToken, tokenData, time.Hour) - require.NoError(t, err) - - userInfo, err := service.UserInfo(ctx, malformedToken) - assert.Error(t, err) - assert.Nil(t, userInfo) - }) - - t.Run("very long access token", func(t *testing.T) { - // Create a very long token - longToken := "very_long_token_" + strings.Repeat("a", 1000) - - userInfo, err := service.UserInfo(ctx, longToken) - assert.Error(t, err) - assert.Nil(t, userInfo) - }) - - t.Run("special characters in token", func(t *testing.T) { - specialToken := "special_token_!@#$%^&*()_+{}[]|\\:;\"'<>?,./`~" - - userInfo, err := service.UserInfo(ctx, specialToken) - assert.Error(t, err) - assert.Nil(t, userInfo) - }) - - t.Run("token with only whitespace", func(t *testing.T) { - whitespaceToken := " \t\n\r " - - userInfo, err := service.UserInfo(ctx, whitespaceToken) - assert.Error(t, err) - assert.Nil(t, userInfo) - }) - - t.Run("token with minimal valid data", func(t *testing.T) { - minimalToken := "minimal_token_999" - testUser := testUsers[9] // test.user - - // Store minimal token data - tokenData := map[string]interface{}{ - "token": minimalToken, - "user_id": testUser.ID, - "subject": testUser.Subject, - "username": testUser.Username, - "exp": time.Now().Add(time.Hour).Unix(), - "iat": time.Now().Unix(), - "token_type": "Bearer", - } - - err := userProvider.StoreToken(minimalToken, tokenData, time.Hour) - require.NoError(t, err) - - userInfo, err := service.UserInfo(ctx, minimalToken) - assert.NoError(t, err) - assert.NotNil(t, userInfo) - - // Verify minimal user info - if userInfoMap, ok := userInfo.(map[string]interface{}); ok { - assert.Equal(t, testUser.Subject, userInfoMap["subject"]) - assert.Equal(t, testUser.Username, userInfoMap["username"]) - } - }) -}