From 44dfb0021a340bc19907fe5e947ef3081bb08c6d Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Sun, 9 Feb 2025 12:04:14 +0000 Subject: [PATCH] map the roles in the JWToken --- src/Yavsc/Config.cs | 3 +- src/Yavsc/Extensions/HostingExtensions.cs | 21 +++++---- src/Yavsc/Services/ProfileService.cs | 56 +++++++++++++++++++++++ src/sampleWebAsWebApiClient/Startup.cs | 20 ++++++-- 4 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 src/Yavsc/Services/ProfileService.cs diff --git a/src/Yavsc/Config.cs b/src/Yavsc/Config.cs index 484c09e3..d1fb8d26 100644 --- a/src/Yavsc/Config.cs +++ b/src/Yavsc/Config.cs @@ -39,6 +39,7 @@ public static class Config { new IdentityResources.OpenId(), new IdentityResources.Profile(), + new IdentityResources.Email() }; public static IEnumerable ApiScopes => @@ -79,7 +80,7 @@ public static class Config AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, - "scope2" } + IdentityServerConstants.StandardScopes.Email } }, }; diff --git a/src/Yavsc/Extensions/HostingExtensions.cs b/src/Yavsc/Extensions/HostingExtensions.cs index 6c4d49b5..b66dba5f 100644 --- a/src/Yavsc/Extensions/HostingExtensions.cs +++ b/src/Yavsc/Extensions/HostingExtensions.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Security.Cryptography.X509Certificates; using Google.Apis.Util.Store; using IdentityServer8; +using IdentityServer8.Services; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.DataProtection; @@ -33,6 +34,7 @@ namespace Yavsc.Extensions; internal static class HostingExtensions { + #region files config public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app, bool enableDirectoryBrowsing = false) { @@ -83,6 +85,9 @@ internal static class HostingExtensions app.UseStaticFiles(); return app; } + + #endregion + public static void ConfigureWorkflow() { foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies()) @@ -163,6 +168,7 @@ internal static class HostingExtensions services.Configure(paypalSection); services.Configure(googleAuthSettings); + services.AddRazorPages(); services.AddSignalR(o => { @@ -197,12 +203,15 @@ services // see https://docs.duendesoftware.com/identityserver/v6/fundamentals/resources/ options.EmitStaticAudienceClaim = true; + options.EmitScopesAsSpaceDelimitedStringInJwt = true; + options.Endpoints.EnableUserInfoEndpoint = true; }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryClients(Config.Clients) .AddInMemoryApiScopes(Config.ApiScopes) .AddAspNetIdentity() ; + services.AddScoped(); if (builder.Environment.IsDevelopment()) { @@ -317,6 +326,7 @@ services _ = services.AddTransient(); _ = services.AddTransient((sp) => new FileDataStore("googledatastore", false)); _ = services.AddTransient(); + services.AddTransient(); // TODO for SMS: services.AddTransient(); @@ -333,9 +343,7 @@ services { options.AddPolicy("ApiScope", policy => { - policy - .RequireAuthenticatedUser() - .RequireClaim("scope", "scope2"); + policy.RequireAuthenticatedUser(); }); options.AddPolicy("AdministratorOnly", policy => { @@ -343,14 +351,11 @@ services }); options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName)); - options.AddPolicy("Bearer", new AuthorizationPolicyBuilder() - .AddAuthenticationSchemes("Bearer") - .RequireAuthenticatedUser().Build()); + // options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456")); // options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement())); options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser()); - options.AddPolicy("IsTheAuthor", policy => - policy.Requirements.Add(new EditPermission())); + options.AddPolicy("IsTheAuthor", policy => policy.Requirements.Add(new EditPermission())); }); services.AddSingleton(); diff --git a/src/Yavsc/Services/ProfileService.cs b/src/Yavsc/Services/ProfileService.cs new file mode 100644 index 00000000..bb6f2d2b --- /dev/null +++ b/src/Yavsc/Services/ProfileService.cs @@ -0,0 +1,56 @@ +using System.Security.Claims; +using IdentityModel; +using IdentityServer8.Models; +using IdentityServer8.Services; +using Microsoft.AspNetCore.Identity; +using Yavsc.Models; + +namespace Yavsc.Services +{ + public class ProfileService : IProfileService + { + private readonly UserManager _userManager; + private readonly RoleManager _roleManager; + public ProfileService( + UserManager userManager, + RoleManager roleManager) + { + _userManager = userManager; + _roleManager = roleManager; + } + + public async Task> GetClaimsFromUserAsync(ApplicationUser user) + { + var claims = new List { + new Claim(JwtClaimTypes.Subject,user.Id.ToString()), + new Claim(JwtClaimTypes.PreferredUserName,user.UserName) + }; + + var role = await _userManager.GetRolesAsync(user); + role.ToList().ForEach(f => + { + claims.Add(new Claim(JwtClaimTypes.Role, f)); + }); + + + return claims; + } + + + public async Task GetProfileDataAsync(ProfileDataRequestContext context) + { + var subjectId = context.Subject.Claims.FirstOrDefault(c => c.Type == "sub").Value; + var user = await _userManager.FindByIdAsync(subjectId); + context.IssuedClaims = await GetClaimsFromUserAsync(user); + } + + + public async Task IsActiveAsync(IsActiveContext context) + { + var subjectId = context.Subject.Claims.FirstOrDefault(c => c.Type == "sub").Value; + var user = await _userManager.FindByIdAsync(subjectId); + context.IsActive = user != null; + } + + } +} diff --git a/src/sampleWebAsWebApiClient/Startup.cs b/src/sampleWebAsWebApiClient/Startup.cs index 8d9a7d81..ccd54c83 100644 --- a/src/sampleWebAsWebApiClient/Startup.cs +++ b/src/sampleWebAsWebApiClient/Startup.cs @@ -1,7 +1,9 @@ +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; public class Startup @@ -25,10 +27,22 @@ using System.IdentityModel.Tokens.Jwt; options.ClientId = "mvc"; options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0"; options.ResponseType = "code"; - - options.Scope.Add("scope2"); + options.UsePkce = true; + options.Scope.Clear(); + options.Scope.Add("openid"); + options.Scope.Add("profile"); + options.Scope.Add("email"); - options.SaveTokens = true; + options.GetClaimsFromUserInfoEndpoint = true; + options.SaveTokens = true; + + options.ClaimActions.MapUniqueJsonKey("role", "role"); + options.ClaimActions.MapUniqueJsonKey("roles", "role"); + options.TokenValidationParameters = new TokenValidationParameters + { + NameClaimType = "name", + RoleClaimType = "role" + }; }); }