diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/.gitignore b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/.gitignore new file mode 100644 index 0000000000..482bfdd002 --- /dev/null +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/.gitignore @@ -0,0 +1,2 @@ +# Duende automatic key management writes signing keys here at first run. +keys/ diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/AddingCustomClaimsToAccessTokenInDuende.sln b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/AddingCustomClaimsToAccessTokenInDuende.sln index 04698f99de..e4e1ef3262 100644 --- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/AddingCustomClaimsToAccessTokenInDuende.sln +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/AddingCustomClaimsToAccessTokenInDuende.sln @@ -9,24 +9,66 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebClient", "WebClient\WebC EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{75F68889-23DC-4EC2-8B78-F5C8DC75FB37}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api", "Api\Api.csproj", "{23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8D058D23-748E-4F5C-A4DA-162526B3934A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8D058D23-748E-4F5C-A4DA-162526B3934A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D058D23-748E-4F5C-A4DA-162526B3934A}.Debug|x64.ActiveCfg = Debug|Any CPU + {8D058D23-748E-4F5C-A4DA-162526B3934A}.Debug|x64.Build.0 = Debug|Any CPU + {8D058D23-748E-4F5C-A4DA-162526B3934A}.Debug|x86.ActiveCfg = Debug|Any CPU + {8D058D23-748E-4F5C-A4DA-162526B3934A}.Debug|x86.Build.0 = Debug|Any CPU {8D058D23-748E-4F5C-A4DA-162526B3934A}.Release|Any CPU.ActiveCfg = Release|Any CPU {8D058D23-748E-4F5C-A4DA-162526B3934A}.Release|Any CPU.Build.0 = Release|Any CPU + {8D058D23-748E-4F5C-A4DA-162526B3934A}.Release|x64.ActiveCfg = Release|Any CPU + {8D058D23-748E-4F5C-A4DA-162526B3934A}.Release|x64.Build.0 = Release|Any CPU + {8D058D23-748E-4F5C-A4DA-162526B3934A}.Release|x86.ActiveCfg = Release|Any CPU + {8D058D23-748E-4F5C-A4DA-162526B3934A}.Release|x86.Build.0 = Release|Any CPU {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Debug|x64.ActiveCfg = Debug|Any CPU + {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Debug|x64.Build.0 = Debug|Any CPU + {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Debug|x86.ActiveCfg = Debug|Any CPU + {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Debug|x86.Build.0 = Debug|Any CPU {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Release|Any CPU.Build.0 = Release|Any CPU + {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Release|x64.ActiveCfg = Release|Any CPU + {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Release|x64.Build.0 = Release|Any CPU + {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Release|x86.ActiveCfg = Release|Any CPU + {EA3E4FDE-43D5-4930-81BC-B3B2BE780714}.Release|x86.Build.0 = Release|Any CPU {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Debug|x64.ActiveCfg = Debug|Any CPU + {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Debug|x64.Build.0 = Debug|Any CPU + {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Debug|x86.ActiveCfg = Debug|Any CPU + {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Debug|x86.Build.0 = Debug|Any CPU {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Release|Any CPU.ActiveCfg = Release|Any CPU {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Release|Any CPU.Build.0 = Release|Any CPU + {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Release|x64.ActiveCfg = Release|Any CPU + {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Release|x64.Build.0 = Release|Any CPU + {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Release|x86.ActiveCfg = Release|Any CPU + {75F68889-23DC-4EC2-8B78-F5C8DC75FB37}.Release|x86.Build.0 = Release|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Debug|x64.ActiveCfg = Debug|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Debug|x64.Build.0 = Debug|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Debug|x86.ActiveCfg = Debug|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Debug|x86.Build.0 = Debug|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Release|Any CPU.Build.0 = Release|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Release|x64.ActiveCfg = Release|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Release|x64.Build.0 = Release|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Release|x86.ActiveCfg = Release|Any CPU + {23F6BA94-D4F4-47B9-9D9F-1FE56FF22428}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/Api.csproj b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/Api.csproj new file mode 100644 index 0000000000..662c1ec715 --- /dev/null +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/Api.csproj @@ -0,0 +1,14 @@ + + + + net10.0 + enable + enable + + + + + + + + diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/Program.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/Program.cs new file mode 100644 index 0000000000..502325cce0 --- /dev/null +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/Program.cs @@ -0,0 +1,30 @@ +using System.Security.Claims; +using Duende.IdentityModel; +using Microsoft.AspNetCore.Authentication.JwtBearer; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.Authority = "https://localhost:5001"; + options.Audience = "paymentsapi"; + options.MapInboundClaims = false; + options.TokenValidationParameters.RoleClaimType = JwtClaimTypes.Role; + }); + +builder.Services.AddAuthorization(); + +var app = builder.Build(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapGet("/discounts", (ClaimsPrincipal user) => new + { + Tenant = user.FindFirstValue("tenant"), + Discount = user.FindFirstValue("payments.discount") + }) + .RequireAuthorization(policy => policy.RequireRole("admin")); + +app.Run(); diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/Properties/launchSettings.json b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/Properties/launchSettings.json new file mode 100644 index 0000000000..b63e1d100e --- /dev/null +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Api": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:5003", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/appsettings.json b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/Api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Config.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Config.cs index c641853de6..da406a9e10 100644 --- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Config.cs +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Config.cs @@ -1,4 +1,5 @@ -using Duende.IdentityServer; +using Duende.IdentityModel; +using Duende.IdentityServer; using Duende.IdentityServer.Models; namespace IdentityServer; @@ -18,6 +19,15 @@ public static class Config new(name: "payments", displayName: "Allow payments", userClaims: new[] { "payments.discount" }) }; + public static IEnumerable ApiResources => + [ + new ApiResource("paymentsapi", "Payments API") + { + Scopes = { "payments" }, + UserClaims = { JwtClaimTypes.Role } + } + ]; + public static IEnumerable Clients => new Client[] { diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/CustomProfileService.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/CustomProfileService.cs index c0ccb0557c..96557caa70 100644 --- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/CustomProfileService.cs +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/CustomProfileService.cs @@ -1,4 +1,5 @@ -using Duende.IdentityServer.Models; +using Duende.IdentityModel; +using Duende.IdentityServer.Models; using Duende.IdentityServer.Services; using System.Security.Claims; using Duende.IdentityServer.Extensions; @@ -7,22 +8,26 @@ namespace IdentityServer; public sealed class CustomProfileService : IProfileService { - public Task GetProfileDataAsync(ProfileDataRequestContext context) + public Task GetProfileDataAsync(ProfileDataRequestContext context, CancellationToken ct) { - if (context.Client.ClientId == "web") + if (context.Application?.Identifier == "web") { context.IssuedClaims.Add(new Claim("tenant", "main")); } if (context.RequestedClaimTypes.Any()) { - context.AddRequestedClaims(new[] { new Claim("payments.discount", "20") }); + context.AddRequestedClaims(new[] + { + new Claim("payments.discount", "20"), + new Claim(JwtClaimTypes.Role, "admin") + }); } return Task.CompletedTask; } - public Task IsActiveAsync(IsActiveContext context) + public Task IsActiveAsync(IsActiveContext context, CancellationToken ct) { if (context.Subject.GetSubjectId() == "3") { diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/HostingExtensions.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/HostingExtensions.cs index 623e19383c..ecde387706 100644 --- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/HostingExtensions.cs +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/HostingExtensions.cs @@ -1,3 +1,4 @@ +using IdentityServer.Pages; using Serilog; namespace IdentityServer; @@ -9,11 +10,12 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde builder.Services.AddIdentityServer(options => { - // https://docs.duendesoftware.com/identityserver/v6/fundamentals/resources/api_scopes#authorization-based-on-scopes + // https://docs.duendesoftware.com/identityserver/fundamentals/resources/api-scopes/ options.EmitStaticAudienceClaim = true; }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) + .AddInMemoryApiResources(Config.ApiResources) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users) .AddProfileService(); diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/IdentityServer.csproj b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/IdentityServer.csproj index 3ce069ee12..fc755165ec 100644 --- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/IdentityServer.csproj +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/IdentityServer.csproj @@ -1,11 +1,12 @@ - + - net8.0 + net10.0 enable + enable - - + + diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Account/AccessDenied.cshtml.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Account/AccessDenied.cshtml.cs index 9247dec033..867b601778 100644 --- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Account/AccessDenied.cshtml.cs +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Account/AccessDenied.cshtml.cs @@ -7,4 +7,4 @@ public class AccessDeniedModel : PageModel public void OnGet() { } -} \ No newline at end of file +} diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Account/Create/Index.cshtml b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Account/Create/Index.cshtml index 2d26395d15..8805ee9dcd 100644 --- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Account/Create/Index.cshtml +++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Account/Create/Index.cshtml @@ -1,5 +1,5 @@ @page -@model IdentityServer.Pages.Create.Index +@model IdentityServer.Pages.Account.Create.Index