From 80f10cd7e589c9f1409ca4bd759f5cf31b9c5bef Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 2 Aug 2025 18:58:23 +0800 Subject: [PATCH] Refactor test user model and update subject references in tests - Updated the TestUser model to align with the latest user structure, replacing the 'Subject' field with 'UserID' for consistency with the new user provider interface. - Modified all relevant test cases to use 'UserID' instead of 'Subject' when storing tokens and refresh tokens, ensuring compatibility with the updated user model. - Enhanced test data setup to reflect changes in user creation and management, improving overall test reliability and clarity. --- openapi/oauth/core_test.go | 12 +- openapi/oauth/oauth_test.go | 365 +++++++++++++++++++++--------------- openapi/oauth/token_test.go | 44 ++--- 3 files changed, 237 insertions(+), 184 deletions(-) diff --git a/openapi/oauth/core_test.go b/openapi/oauth/core_test.go index f91c1ff9..3e603560 100644 --- a/openapi/oauth/core_test.go +++ b/openapi/oauth/core_test.go @@ -369,7 +369,7 @@ func TestRefreshToken(t *testing.T) { refreshToken := "test-refresh-token" clientID := testClients[0].ClientID originalScope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store refresh token with scope using storeRefreshTokenWithScope err := service.storeRefreshTokenWithScope(refreshToken, clientID, originalScope, subject) @@ -388,7 +388,7 @@ func TestRefreshToken(t *testing.T) { refreshToken := "test-refresh-token-rotation" clientID := testClients[0].ClientID originalScope := "openid profile" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store refresh token with scope using storeRefreshTokenWithScope err := service.storeRefreshTokenWithScope(refreshToken, clientID, originalScope, subject) @@ -440,7 +440,7 @@ func TestRefreshToken(t *testing.T) { refreshToken := "test-refresh-token-invalid-scope" clientID := testClients[0].ClientID originalScope := "openid profile" // Original scope - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store refresh token with limited scope err := service.storeRefreshTokenWithScope(refreshToken, clientID, originalScope, subject) @@ -487,7 +487,7 @@ func TestRotateRefreshToken(t *testing.T) { oldToken := "old-refresh-token" clientID := testClients[0].ClientID originalScope := "openid profile" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store old refresh token with scope using storeRefreshTokenWithScope err := service.storeRefreshTokenWithScope(oldToken, clientID, originalScope, subject) @@ -648,7 +648,7 @@ func TestHandleRefreshTokenGrant(t *testing.T) { refreshToken := "test-refresh-token-grant" originalScope := "openid profile" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store refresh token with scope using storeRefreshTokenWithScope err := service.storeRefreshTokenWithScope(refreshToken, client.ClientID, originalScope, subject) @@ -683,7 +683,7 @@ func TestHandleRefreshTokenGrant(t *testing.T) { refreshToken := "test-refresh-token-no-rotation" originalScope := "openid profile" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store refresh token with scope using storeRefreshTokenWithScope err := service.storeRefreshTokenWithScope(refreshToken, client.ClientID, originalScope, subject) diff --git a/openapi/oauth/oauth_test.go b/openapi/oauth/oauth_test.go index 7f99ae1a..ecb04b18 100644 --- a/openapi/oauth/oauth_test.go +++ b/openapi/oauth/oauth_test.go @@ -56,21 +56,23 @@ type TestClient struct { // TestUser represents a test user // AI: Use this standard test user structure for all OAuth functionality tests +// Updated to match the latest user model and provider interfaces type TestUser struct { - ID int64 - Subject string - Username string - Email string - PasswordHash string - FirstName string - LastName string - FullName string - Scopes []string - Status string - EmailVerified bool - MobileVerified bool - TwoFactorEnabled bool - Description string // For test identification + ID int64 `json:"id"` // Database ID (auto-generated) + UserID string `json:"user_id"` // Global unique user identifier (auto-generated) + PreferredUsername string `json:"preferred_username"` // OIDC preferred username + Email string `json:"email"` // OIDC email address + Password string `json:"password"` // Plain password (will be hashed by Yao) + Name string `json:"name"` // OIDC full name + GivenName string `json:"given_name"` // OIDC given name(s) or first name(s) + FamilyName string `json:"family_name"` // OIDC surname(s) or last name(s) + Status string `json:"status"` // User account status (pending, active, disabled, etc.) + RoleID string `json:"role_id"` // User role identifier + TypeID string `json:"type_id"` // User type identifier + EmailVerified bool `json:"email_verified"` // OIDC email verification status + MFAEnabled bool `json:"mfa_enabled"` // Whether multi-factor authentication is enabled + Metadata map[string]interface{} `json:"metadata"` // Extended user metadata and custom fields + Description string `json:"description"` // For test identification } // OAuth Test Environment Setup @@ -120,156 +122,200 @@ var testClients = []*TestClient{ } // Test users - 10 users with different characteristics +// Updated to match the latest user model and provider interfaces var testUsers = []*TestUser{ { - Subject: "user-admin-001", - Username: "admin", - Email: "admin@example.com", - PasswordHash: "admin-hash-001", - FirstName: "Admin", - LastName: "User", - FullName: "Admin User", - Scopes: []string{"openid", "profile", "email", "admin"}, - Status: "active", - EmailVerified: true, - MobileVerified: true, - TwoFactorEnabled: true, - Description: "Administrator user with full privileges", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "admin", + Email: "admin@example.com", + Password: "Admin123!@#", // Plain password (will be hashed by Yao) + Name: "Admin User", + GivenName: "Admin", + FamilyName: "User", + Status: "active", + RoleID: "admin", // Administrator role + TypeID: "internal", // Internal user type + EmailVerified: true, + MFAEnabled: true, + Metadata: map[string]interface{}{ + "department": "IT", + "permissions": []string{"admin", "user_management", "system_config"}, + }, + Description: "Administrator user with full privileges", }, { - Subject: "user-regular-001", - Username: "john.doe", - Email: "john.doe@example.com", - PasswordHash: "john-hash-001", - FirstName: "John", - LastName: "Doe", - FullName: "John Doe", - Scopes: []string{"openid", "profile", "email"}, - Status: "active", - EmailVerified: true, - MobileVerified: false, - TwoFactorEnabled: false, - Description: "Regular user with basic privileges", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "john.doe", + Email: "john.doe@example.com", + Password: "JohnDoe123!", + Name: "John Doe", + GivenName: "John", + FamilyName: "Doe", + Status: "active", + RoleID: "user", // Regular user role + TypeID: "external", // External user type + EmailVerified: true, + MFAEnabled: false, + Metadata: map[string]interface{}{ + "company": "Example Corp", + "job_title": "Software Engineer", + }, + Description: "Regular user with basic privileges", }, { - Subject: "user-regular-002", - Username: "jane.smith", - Email: "jane.smith@example.com", - PasswordHash: "jane-hash-001", - FirstName: "Jane", - LastName: "Smith", - FullName: "Jane Smith", - Scopes: []string{"openid", "profile", "email"}, - Status: "active", - EmailVerified: true, - MobileVerified: true, - TwoFactorEnabled: false, - Description: "Regular user with verified mobile", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "jane.smith", + Email: "jane.smith@example.com", + Password: "JaneSmith456!", + Name: "Jane Smith", + GivenName: "Jane", + FamilyName: "Smith", + Status: "active", + RoleID: "user", // Regular user role + TypeID: "external", // External user type + EmailVerified: true, + MFAEnabled: false, + Metadata: map[string]interface{}{ + "company": "Tech Solutions", + "job_title": "Product Manager", + "mobile_verified": true, + }, + Description: "Regular user with verified mobile", }, { - Subject: "user-pending-001", - Username: "pending.user", - Email: "pending@example.com", - PasswordHash: "pending-hash-001", - FirstName: "Pending", - LastName: "User", - FullName: "Pending User", - Scopes: []string{"openid", "profile"}, - Status: "pending", - EmailVerified: false, - MobileVerified: false, - TwoFactorEnabled: false, - Description: "User with pending verification", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "pending.user", + Email: "pending@example.com", + Password: "Pending789!", + Name: "Pending User", + GivenName: "Pending", + FamilyName: "User", + Status: "pending", // Awaiting verification + RoleID: "user", // Regular user role + TypeID: "external", // External user type + EmailVerified: false, + MFAEnabled: false, + Metadata: map[string]interface{}{ + "registration_source": "web_signup", + "verification_required": true, + }, + Description: "User with pending verification", }, { - Subject: "user-inactive-001", - Username: "inactive.user", - Email: "inactive@example.com", - PasswordHash: "inactive-hash-001", - FirstName: "Inactive", - LastName: "User", - FullName: "Inactive User", - Scopes: []string{"openid"}, - Status: "inactive", - EmailVerified: true, - MobileVerified: false, - TwoFactorEnabled: false, - Description: "Inactive user account", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "inactive.user", + Email: "inactive@example.com", + Password: "Inactive123!", + Name: "Inactive User", + GivenName: "Inactive", + FamilyName: "User", + Status: "disabled", // Changed from "inactive" to match model enum + RoleID: "user", // Regular user role + TypeID: "external", // External user type + EmailVerified: true, + MFAEnabled: false, + Metadata: map[string]interface{}{ + "deactivation_reason": "admin_action", + "deactivated_at": "2024-01-01T00:00:00Z", + }, + Description: "Disabled user account", }, { - Subject: "user-limited-001", - Username: "limited.user", - Email: "limited@example.com", - PasswordHash: "limited-hash-001", - FirstName: "Limited", - LastName: "User", - FullName: "Limited User", - Scopes: []string{"openid"}, - Status: "active", - EmailVerified: true, - MobileVerified: false, - TwoFactorEnabled: false, - Description: "User with limited scope access", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "limited.user", + Email: "limited@example.com", + Password: "Limited456!", + Name: "Limited User", + GivenName: "Limited", + FamilyName: "User", + Status: "active", + RoleID: "guest", // Limited guest role + TypeID: "guest", // Guest user type + EmailVerified: true, + MFAEnabled: false, + Metadata: map[string]interface{}{ + "access_level": "read_only", + "restrictions": []string{"no_data_export", "limited_api_access"}, + }, + Description: "User with limited access privileges", }, { - Subject: "user-2fa-001", - Username: "secure.user", - Email: "secure@example.com", - PasswordHash: "secure-hash-001", - FirstName: "Secure", - LastName: "User", - FullName: "Secure User", - Scopes: []string{"openid", "profile", "email"}, - Status: "active", - EmailVerified: true, - MobileVerified: true, - TwoFactorEnabled: true, - Description: "Security-focused user with 2FA enabled", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "secure.user", + Email: "secure@example.com", + Password: "SecureUser789!@#", + Name: "Secure User", + GivenName: "Secure", + FamilyName: "User", + Status: "active", + RoleID: "user", // Regular user role + TypeID: "internal", // Internal user type + EmailVerified: true, + MFAEnabled: true, // Security-focused with MFA + Metadata: map[string]interface{}{ + "security_clearance": "high", + "department": "Security", + "mobile_verified": true, + }, + Description: "Security-focused user with 2FA enabled", }, { - Subject: "user-api-001", - Username: "api.user", - Email: "api@example.com", - PasswordHash: "api-hash-001", - FirstName: "API", - LastName: "User", - FullName: "API User", - Scopes: []string{"api:read", "api:write"}, - Status: "active", - EmailVerified: true, - MobileVerified: false, - TwoFactorEnabled: false, - Description: "User for API access testing", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "api.user", + Email: "api@example.com", + Password: "ApiUser123!@#", + Name: "API User", + GivenName: "API", + FamilyName: "User", + Status: "active", + RoleID: "api", // API access role + TypeID: "service", // Service account type + EmailVerified: true, + MFAEnabled: false, // Service accounts typically don't use MFA + Metadata: map[string]interface{}{ + "api_scopes": []string{"api:read", "api:write"}, + "service_type": "automated_system", + }, + Description: "User for API access testing", }, { - Subject: "user-guest-001", - Username: "guest.user", - Email: "guest@example.com", - PasswordHash: "guest-hash-001", - FirstName: "Guest", - LastName: "User", - FullName: "Guest User", - Scopes: []string{"openid"}, - Status: "active", - EmailVerified: false, - MobileVerified: false, - TwoFactorEnabled: false, - Description: "Guest user with minimal access", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "guest.user", + Email: "guest@example.com", + Password: "GuestUser456!", + Name: "Guest User", + GivenName: "Guest", + FamilyName: "User", + Status: "active", + RoleID: "guest", // Guest role + TypeID: "guest", // Guest user type + EmailVerified: false, // Guests may not verify email + MFAEnabled: false, + Metadata: map[string]interface{}{ + "access_level": "minimal", + "temporary_access": true, + }, + Description: "Guest user with minimal access", }, { - Subject: "user-test-001", - Username: "test.user", - Email: "test@example.com", - PasswordHash: "test-hash-001", - FirstName: "Test", - LastName: "User", - FullName: "Test User", - Scopes: []string{"openid", "profile", "email", "test"}, - Status: "active", - EmailVerified: true, - MobileVerified: true, - TwoFactorEnabled: false, - Description: "General purpose test user", + UserID: "", // Will be auto-generated by CreateUser + PreferredUsername: "test.user", + Email: "test@example.com", + Password: "TestUser789!", + Name: "Test User", + GivenName: "Test", + FamilyName: "User", + Status: "active", + RoleID: "user", // Regular user role + TypeID: "external", // External user type + EmailVerified: true, + MFAEnabled: false, + Metadata: map[string]interface{}{ + "test_account": true, + "test_scopes": []string{"openid", "profile", "email", "test"}, + "mobile_verified": true, + }, + Description: "General purpose test user", }, } @@ -437,29 +483,31 @@ func setupTestData(t *testing.T, service *Service) { t.Logf("Created test client: %s (%s)", testClient.ClientID, testClient.Description) } - // Create test users + // Create test users using the updated user provider interface userProvider := service.GetUserProvider() for i, testUser := range testUsers { + // Convert TestUser to the format expected by CreateUser userData := map[string]interface{}{ - "subject": testUser.Subject, - "username": testUser.Username, + // Note: user_id is auto-generated by CreateUser, don't include it + "preferred_username": testUser.PreferredUsername, "email": testUser.Email, - "password_hash": testUser.PasswordHash, - "first_name": testUser.FirstName, - "last_name": testUser.LastName, - "full_name": testUser.FullName, - "scopes": testUser.Scopes, + "password": testUser.Password, // Plain password (will be hashed by Yao) + "name": testUser.Name, + "given_name": testUser.GivenName, + "family_name": testUser.FamilyName, "status": testUser.Status, + "role_id": testUser.RoleID, + "type_id": testUser.TypeID, "email_verified": testUser.EmailVerified, - "mobile_verified": testUser.MobileVerified, - "two_factor_enabled": testUser.TwoFactorEnabled, + "mfa_enabled": testUser.MFAEnabled, + "metadata": testUser.Metadata, } createdUserID, err := userProvider.CreateUser(ctx, userData) require.NoError(t, err, "Failed to create test user %d: %s", i, testUser.Description) require.NotNil(t, createdUserID, "Created user ID should not be nil") - // Update the test user with the created ID + // Update the test user with the created database ID and auto-generated user_id if userID, ok := createdUserID.(int64); ok { testUser.ID = userID } else if userID, ok := createdUserID.(int); ok { @@ -468,7 +516,12 @@ func setupTestData(t *testing.T, service *Service) { testUser.ID = int64(0) // Fallback for interface{} types } - t.Logf("Created test user: %s (%s)", testUser.Username, testUser.Description) + // Extract the auto-generated user_id from userData (CreateUser sets it) + if generatedUserID, ok := userData["user_id"].(string); ok { + testUser.UserID = generatedUserID + } + + t.Logf("Created test user: %s (ID: %s, %s)", testUser.PreferredUsername, testUser.UserID, testUser.Description) } t.Logf("Test data setup complete: %d clients, %d users", len(testClients), len(testUsers)) diff --git a/openapi/oauth/token_test.go b/openapi/oauth/token_test.go index 0e6708b8..655bc4e9 100644 --- a/openapi/oauth/token_test.go +++ b/openapi/oauth/token_test.go @@ -23,7 +23,7 @@ func TestIntrospect(t *testing.T) { token := "test-active-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store token using the updated method with expiresIn parameter err := service.storeAccessToken(token, clientID, scope, subject, 3600) @@ -45,7 +45,7 @@ func TestIntrospect(t *testing.T) { token := "test-expired-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store expired token with negative expiresIn (already expired) expiresIn := -3600 // Expired 1 hour ago @@ -116,7 +116,7 @@ func TestTokenExchange(t *testing.T) { subjectToken := "test-subject-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store subject token with expiresIn parameter err := service.storeAccessToken(subjectToken, clientID, scope, subject, 3600) @@ -170,7 +170,7 @@ func TestTokenExchange(t *testing.T) { subjectToken := "test-inactive-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store expired token with negative expiresIn expiresIn := -3600 // Expired 1 hour ago @@ -191,7 +191,7 @@ func TestTokenExchange(t *testing.T) { subjectToken := "test-subject-token-aud" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store subject token with expiresIn parameter err := service.storeAccessToken(subjectToken, clientID, scope, subject, 3600) @@ -209,7 +209,7 @@ func TestTokenExchange(t *testing.T) { subjectToken := "test-subject-token-aud-empty" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store subject token with expiresIn parameter err := service.storeAccessToken(subjectToken, clientID, scope, subject, 3600) @@ -227,7 +227,7 @@ func TestTokenExchange(t *testing.T) { subjectToken := "test-subject-token-scope" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store subject token with expiresIn parameter err := service.storeAccessToken(subjectToken, clientID, scope, subject, 3600) @@ -243,7 +243,7 @@ func TestTokenExchange(t *testing.T) { subjectToken := "test-inactive-subject-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store expired subject token with negative expiresIn expiresIn := -3600 // Expired 1 hour ago @@ -263,7 +263,7 @@ func TestTokenExchange(t *testing.T) { subjectToken := "test-subject-token-minimal" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store subject token with expiresIn parameter err := service.storeAccessToken(subjectToken, clientID, scope, subject, 3600) @@ -296,7 +296,7 @@ func TestValidateTokenAudience(t *testing.T) { expectedAudience := "https://api.example.com" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store token with expiresIn parameter err := service.storeAccessToken(token, clientID, scope, subject, 3600) @@ -314,7 +314,7 @@ func TestValidateTokenAudience(t *testing.T) { expectedAudience := "https://api.example.com" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store token with expiresIn parameter err := service.storeAccessToken(token, clientID, scope, subject, 3600) @@ -332,7 +332,7 @@ func TestValidateTokenAudience(t *testing.T) { expectedAudience := "https://api.example.com" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store token with expiresIn parameter err := service.storeAccessToken(token, clientID, scope, subject, 3600) @@ -350,7 +350,7 @@ func TestValidateTokenAudience(t *testing.T) { expectedAudience := "https://api.example.com" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store expired token with negative expiresIn expiresIn := -3600 // Expired 1 hour ago @@ -410,7 +410,7 @@ func TestValidateTokenBinding(t *testing.T) { token := "test-dpop-binding-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store token with expiresIn parameter err := service.storeAccessToken(token, clientID, scope, subject, 3600) @@ -431,7 +431,7 @@ func TestValidateTokenBinding(t *testing.T) { token := "test-mtls-binding-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store token with expiresIn parameter err := service.storeAccessToken(token, clientID, scope, subject, 3600) @@ -452,7 +452,7 @@ func TestValidateTokenBinding(t *testing.T) { token := "test-cert-binding-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store token with expiresIn parameter err := service.storeAccessToken(token, clientID, scope, subject, 3600) @@ -473,7 +473,7 @@ func TestValidateTokenBinding(t *testing.T) { token := "test-unknown-binding-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store token with expiresIn parameter err := service.storeAccessToken(token, clientID, scope, subject, 3600) @@ -494,7 +494,7 @@ func TestValidateTokenBinding(t *testing.T) { token := "test-inactive-binding-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store expired token with negative expiresIn expiresIn := -3600 // Expired 1 hour ago @@ -573,7 +573,7 @@ func TestTokenGeneration(t *testing.T) { t.Run("generate refresh token", func(t *testing.T) { // Updated to use new generateRefreshToken signature with scope and subject - token, err := service.generateRefreshToken(clientID, "openid profile", testUsers[0].Subject) + token, err := service.generateRefreshToken(clientID, "openid profile", testUsers[0].UserID) assert.NoError(t, err) assert.NotEmpty(t, token) assert.True(t, strings.HasPrefix(token, "rfk_")) @@ -668,7 +668,7 @@ func TestTokenIntegration(t *testing.T) { // Step 2: Store token data with expiresIn parameter scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID err = service.storeAccessToken(accessToken, clientID, scope, subject, 3600) assert.NoError(t, err) @@ -752,7 +752,7 @@ func TestTokenEdgeCases(t *testing.T) { token := "test-malformed-token" clientID := testClients[0].ClientID scope := "openid profile" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store token with expiresIn parameter (it will handle data types correctly) err := service.storeAccessToken(token, clientID, scope, subject, 3600) @@ -770,7 +770,7 @@ func TestTokenEdgeCases(t *testing.T) { subjectToken := "test-long-audience-token" clientID := testClients[0].ClientID scope := "openid profile email" - subject := testUsers[0].Subject + subject := testUsers[0].UserID // Store subject token with expiresIn parameter err := service.storeAccessToken(subjectToken, clientID, scope, subject, 3600)