From 1a5ce8a004d54b7465367d9a50124eb1eb82e76b Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Sat, 28 Mar 2026 11:23:40 +0000 Subject: [PATCH 1/2] Refactor OIDC scopes, claims, and test data tables - Refactored test data table variable names in Users.feature.cs for clarity and ensured correct tables are used in test steps. - Improved client credentials grant in OidcEndpoints.cs by resolving granted scopes before resource listing and principal creation. - Added ClaimTypes.NameIdentifier to user claims in OidcHelpers.cs for better compatibility. - Registered ClaimsTransformer as IClaimsTransformation in Program.cs. - Cleaned up unused using and formatting in GrantService.cs. - Updated appsettings.json to use the correct SecurityService database. --- .../Users/Users.feature.cs | 48 +++++++++---------- SecurityService/Oidc/OidcEndpoints.cs | 8 ++-- SecurityService/Oidc/OidcHelpers.cs | 3 +- SecurityService/Program.cs | 1 + SecurityService/Services/GrantService.cs | 5 +- SecurityService/appsettings.json | 12 +---- 6 files changed, 34 insertions(+), 43 deletions(-) diff --git a/SecurityService.IntegrationTests/Users/Users.feature.cs b/SecurityService.IntegrationTests/Users/Users.feature.cs index c73ac1ab..5b2e97e3 100644 --- a/SecurityService.IntegrationTests/Users/Users.feature.cs +++ b/SecurityService.IntegrationTests/Users/Users.feature.cs @@ -113,16 +113,16 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa { #line 4 #line hidden - global::Reqnroll.Table table29 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table1 = new global::Reqnroll.Table(new string[] { "Role Name"}); - table29.AddRow(new string[] { + table1.AddRow(new string[] { "TestRole1"}); - table29.AddRow(new string[] { + table1.AddRow(new string[] { "TestRole2"}); - table29.AddRow(new string[] { + table1.AddRow(new string[] { "TestRole3"}); #line 5 - await testRunner.GivenAsync("I create the following roles", ((string)(null)), table29, "Given "); + await testRunner.GivenAsync("I create the following roles", ((string)(null)), table1, "Given "); #line hidden } @@ -156,7 +156,7 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa #line 4 await this.FeatureBackgroundAsync(); #line hidden - global::Reqnroll.Table table30 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table2 = new global::Reqnroll.Table(new string[] { "Email Address", "Phone Number", "Given Name", @@ -164,7 +164,7 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa "Family Name", "Claims", "Roles"}); - table30.AddRow(new string[] { + table2.AddRow(new string[] { "testuser1@testing.co.uk", "123456789", "Test", @@ -172,7 +172,7 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa "User 1", "", "TestRole1"}); - table30.AddRow(new string[] { + table2.AddRow(new string[] { "testuser2@testing.co.uk", "123456789", "Test", @@ -180,7 +180,7 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa "User 2", "", "TestRole2"}); - table30.AddRow(new string[] { + table2.AddRow(new string[] { "testuser3@testing.co.uk", "123456789", "Test", @@ -189,9 +189,9 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa "", "TestRole3"}); #line 13 - await testRunner.GivenAsync("I create the following users", ((string)(null)), table30, "Given "); + await testRunner.GivenAsync("I create the following users", ((string)(null)), table2, "Given "); #line hidden - global::Reqnroll.Table table31 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table3 = new global::Reqnroll.Table(new string[] { "Email Address", "Phone Number", "Given Name", @@ -200,7 +200,7 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa "Claims", "Roles", "RegistrationDate"}); - table31.AddRow(new string[] { + table3.AddRow(new string[] { "testuser1@testing.co.uk", "123456789", "Test", @@ -211,9 +211,9 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo, globa "Today"}); #line 18 await testRunner.WhenAsync("I get the user with user name \'testuser1@testing.co.uk\' the user details are retu" + - "rned as follows", ((string)(null)), table31, "When "); + "rned as follows", ((string)(null)), table3, "When "); #line hidden - global::Reqnroll.Table table32 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table4 = new global::Reqnroll.Table(new string[] { "Email Address", "Phone Number", "Given Name", @@ -222,7 +222,7 @@ await testRunner.WhenAsync("I get the user with user name \'testuser1@testing.co "Claims", "Roles", "RegistrationDate"}); - table32.AddRow(new string[] { + table4.AddRow(new string[] { "testuser2@testing.co.uk", "123456789", "Test", @@ -233,9 +233,9 @@ await testRunner.WhenAsync("I get the user with user name \'testuser1@testing.co "Today"}); #line 21 await testRunner.WhenAsync("I get the user with user name \'testuser2@testing.co.uk\' the user details are retu" + - "rned as follows", ((string)(null)), table32, "When "); + "rned as follows", ((string)(null)), table4, "When "); #line hidden - global::Reqnroll.Table table33 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table5 = new global::Reqnroll.Table(new string[] { "Email Address", "Phone Number", "Given Name", @@ -244,7 +244,7 @@ await testRunner.WhenAsync("I get the user with user name \'testuser2@testing.co "Claims", "Roles", "RegistrationDate"}); - table33.AddRow(new string[] { + table5.AddRow(new string[] { "testuser3@testing.co.uk", "123456789", "Test", @@ -255,9 +255,9 @@ await testRunner.WhenAsync("I get the user with user name \'testuser2@testing.co "Today"}); #line 24 await testRunner.WhenAsync("I get the user with user name \'testuser3@testing.co.uk\' the user details are retu" + - "rned as follows", ((string)(null)), table33, "When "); + "rned as follows", ((string)(null)), table5, "When "); #line hidden - global::Reqnroll.Table table34 = new global::Reqnroll.Table(new string[] { + global::Reqnroll.Table table6 = new global::Reqnroll.Table(new string[] { "Email Address", "Phone Number", "Given Name", @@ -266,7 +266,7 @@ await testRunner.WhenAsync("I get the user with user name \'testuser3@testing.co "Claims", "Roles", "RegistrationDate"}); - table34.AddRow(new string[] { + table6.AddRow(new string[] { "testuser1@testing.co.uk", "123456789", "Test", @@ -275,7 +275,7 @@ await testRunner.WhenAsync("I get the user with user name \'testuser3@testing.co "emailaddress:testuser1@testing.co.uk, givenname:Test, surname:User 1", "TestRole1", "Today"}); - table34.AddRow(new string[] { + table6.AddRow(new string[] { "testuser2@testing.co.uk", "123456789", "Test", @@ -284,7 +284,7 @@ await testRunner.WhenAsync("I get the user with user name \'testuser3@testing.co "emailaddress:testuser2@testing.co.uk, givenname:Test, surname:User 2", "TestRole2", "Today"}); - table34.AddRow(new string[] { + table6.AddRow(new string[] { "testuser3@testing.co.uk", "123456789", "Test", @@ -294,7 +294,7 @@ await testRunner.WhenAsync("I get the user with user name \'testuser3@testing.co "TestRole3", "Today"}); #line 27 - await testRunner.WhenAsync("I get the users 3 users details are returned as follows", ((string)(null)), table34, "When "); + await testRunner.WhenAsync("I get the users 3 users details are returned as follows", ((string)(null)), table6, "When "); #line hidden } await this.ScenarioCleanupAsync(); diff --git a/SecurityService/Oidc/OidcEndpoints.cs b/SecurityService/Oidc/OidcEndpoints.cs index e8eeae33..818cf461 100644 --- a/SecurityService/Oidc/OidcEndpoints.cs +++ b/SecurityService/Oidc/OidcEndpoints.cs @@ -138,7 +138,7 @@ public static async Task TokenAsync( { return InvalidGrant("The token is no longer valid."); } - + var principal = await OidcHelpers.CreatePrincipalAsync( user, userManager, @@ -175,8 +175,10 @@ await scopeManager.ListResourcesAsync(ImmutableArray.CreateRange(authenticationR return InvalidGrant(); } - var resources = await scopeManager.ListResourcesAsync(ImmutableArray.CreateRange(request.GetScopes()), cancellationToken).ToListAsync(cancellationToken); - var principal = await OidcHelpers.CreatePrincipalAsync(user, userManager, request.GetScopes(), resources, authorizationId: null); + var grantedScopes = await ResolveClientCredentialsScopesAsync(request, dbContext, cancellationToken); + + var resources = await scopeManager.ListResourcesAsync(ImmutableArray.CreateRange(grantedScopes), cancellationToken).ToListAsync(cancellationToken); + var principal = await OidcHelpers.CreatePrincipalAsync(user, userManager, grantedScopes, resources, authorizationId: null); return Results.SignIn(principal, authenticationScheme: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } diff --git a/SecurityService/Oidc/OidcHelpers.cs b/SecurityService/Oidc/OidcHelpers.cs index a2a33062..0cf8894a 100644 --- a/SecurityService/Oidc/OidcHelpers.cs +++ b/SecurityService/Oidc/OidcHelpers.cs @@ -50,7 +50,8 @@ public static async Task CreatePrincipalAsync( identity.SetClaim(Claims.Subject, user.Id) .SetClaim(Claims.Email, user.Email) .SetClaim(Claims.Name, user.UserName) - .SetClaim(Claims.PreferredUsername, user.UserName); + .SetClaim(Claims.PreferredUsername, user.UserName) + .SetClaim(ClaimTypes.NameIdentifier, user.Id); if (string.IsNullOrWhiteSpace(user.GivenName) == false) { diff --git a/SecurityService/Program.cs b/SecurityService/Program.cs index 6943f798..1bc25318 100644 --- a/SecurityService/Program.cs +++ b/SecurityService/Program.cs @@ -119,6 +119,7 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddTransient(); builder.Services.AddIdentity(identityOptions => { diff --git a/SecurityService/Services/GrantService.cs b/SecurityService/Services/GrantService.cs index ae718ad7..25c2f3c6 100644 --- a/SecurityService/Services/GrantService.cs +++ b/SecurityService/Services/GrantService.cs @@ -3,7 +3,6 @@ using SecurityService.Models; using SimpleResults; using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; using static OpenIddict.Abstractions.OpenIddictConstants; namespace SecurityService.Services; @@ -76,6 +75,4 @@ public async Task RevokeAsync(string userId, string authorizationId, Can ? Result.Success() : Result.Failure("The authorization could not be revoked."); } -} - - +} \ No newline at end of file diff --git a/SecurityService/appsettings.json b/SecurityService/appsettings.json index 576c043c..889cf357 100644 --- a/SecurityService/appsettings.json +++ b/SecurityService/appsettings.json @@ -1,17 +1,7 @@ { "ConnectionStrings": { - "AuthenticationDbContext": "Server=127.0.0.1;Database=NewSecurityService;user id=sa;password=sp1ttal;Encrypt=false" + "AuthenticationDbContext": "Server=127.0.0.1;Database=SecurityService;user id=sa;password=sp1ttal;Encrypt=false" }, - //"SecurityService": { - // "UseInMemoryDatabase": false, - // "InMemoryDatabaseName": "NewSecurityService", - // "DatabaseProvider": "SqlServer", - // "IssuerUrl": "https://localhost:5001/", - // "SeedDefaultScopes": true, - // "PublicOrigin": "https://localhost:5001/", - // "ExternalProviders": [], - // "SignInOptions" - //}, "ServiceOptions": { "SeedDefaultScopes": false, "PublicOrigin": "https://127.0.0.1:50001", From cf5f1c195a3b44ff2f89a88bcbb226225809ad14 Mon Sep 17 00:00:00 2001 From: Stuart Ferguson Date: Sat, 28 Mar 2026 11:29:10 +0000 Subject: [PATCH 2/2] oops --- SecurityService/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/SecurityService/Program.cs b/SecurityService/Program.cs index 1bc25318..6943f798 100644 --- a/SecurityService/Program.cs +++ b/SecurityService/Program.cs @@ -119,7 +119,6 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); -builder.Services.AddTransient(); builder.Services.AddIdentity(identityOptions => {