Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Duende automatic key management writes signing keys here at first run.
keys/
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Duende.IdentityModel" Version="8.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -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();
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Duende.IdentityServer;
using Duende.IdentityModel;
using Duende.IdentityServer;
using Duende.IdentityServer.Models;

namespace IdentityServer;
Expand All @@ -18,6 +19,15 @@ public static class Config
new(name: "payments", displayName: "Allow payments", userClaims: new[] { "payments.discount" })
};

public static IEnumerable<ApiResource> ApiResources =>
[
new ApiResource("paymentsapi", "Payments API")
{
Scopes = { "payments" },
UserClaims = { JwtClaimTypes.Role }
}
];

public static IEnumerable<Client> Clients =>
new Client[]
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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")
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using IdentityServer.Pages;
using Serilog;

namespace IdentityServer;
Expand All @@ -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<CustomProfileService>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Duende.IdentityServer" Version="6.3.7" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
<PackageReference Include="Duende.IdentityServer" Version="8.0.5" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ public class AccessDeniedModel : PageModel
public void OnGet()
{
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page
@model IdentityServer.Pages.Create.Index
@model IdentityServer.Pages.Account.Create.Index

<div class="login-page">
<div class="lead">
Expand All @@ -14,19 +14,19 @@
<form asp-page="/Account/Create/Index">
<input type="hidden" asp-for="Input.ReturnUrl" />

<div class="form-group">
<div class="mb-3">
<label asp-for="Input.Username"></label>
<input class="form-control" placeholder="Username" asp-for="Input.Username" autofocus>
</div>
<div class="form-group">
<div class="mb-3">
<label asp-for="Input.Password"></label>
<input type="password" class="form-control" placeholder="Password" asp-for="Input.Password" autocomplete="off">
</div>
<div class="form-group">
<div class="mb-3">
<label asp-for="Input.Name"></label>
<input type="text" class="form-control" placeholder="Name" asp-for="Input.Name">
</div>
<div class="form-group">
<div class="mb-3">
<label asp-for="Input.Email"></label>
<input type="email" class="form-control" placeholder="Email" asp-for="Input.Email" >
</div>
Expand All @@ -37,4 +37,4 @@
</div>

</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,46 +1,37 @@
using Duende.IdentityServer;
using Duende.IdentityServer.Events;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using Duende.IdentityServer.Stores;
using Duende.IdentityServer.Test;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace IdentityServer.Pages.Create;
namespace IdentityServer.Pages.Account.Create;

[SecurityHeaders]
[AllowAnonymous]
public class Index : PageModel
public class Index(
IIdentityServerInteractionService interaction,
TestUserStore? users = null)
: PageModel
{
private readonly TestUserStore _users;
private readonly IIdentityServerInteractionService _interaction;
private readonly TestUserStore _users = users ?? throw new InvalidOperationException("Please call 'AddTestUsers(TestUsers.Users)' on the IIdentityServerBuilder in Startup or remove the TestUserStore from the AccountController.");

[BindProperty]
public InputModel Input { get; set; }

public Index(
IIdentityServerInteractionService interaction,
TestUserStore users = null)
{
// this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity)
_users = users ?? throw new Exception("Please call 'AddTestUsers(TestUsers.Users)' on the IIdentityServerBuilder in Startup or remove the TestUserStore from the AccountController.");

_interaction = interaction;
}
public InputModel Input { get; set; } = default!;

public IActionResult OnGet(string returnUrl)
// this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity)

public IActionResult OnGet(string? returnUrl)
{
Input = new InputModel { ReturnUrl = returnUrl };
return Page();
}
public async Task<IActionResult> OnPost()

public async Task<IActionResult> OnPostAsync(CancellationToken ct)
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);
var context = await interaction.GetAuthorizationContextAsync(Input.ReturnUrl, ct);

// the user clicked the "cancel" button
if (Input.Button != "create")
Expand All @@ -50,7 +41,7 @@ public async Task<IActionResult> OnPost()
// if the user cancels, send a result back into IdentityServer as if they
// denied the consent (even if this client does not require consent).
// this will send back an access denied OIDC error response to the client.
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);
await interaction.DenyAuthorizationAsync(context, InteractionError.AccessDenied, ct);

// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (context.IsNativeClient())
Expand All @@ -60,7 +51,7 @@ public async Task<IActionResult> OnPost()
return this.LoadingPage(Input.ReturnUrl);
}

return Redirect(Input.ReturnUrl);
return Redirect(Input.ReturnUrl ?? "~/");
}
else
{
Expand Down Expand Up @@ -95,8 +86,8 @@ public async Task<IActionResult> OnPost()
return this.LoadingPage(Input.ReturnUrl);
}

// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
return Redirect(Input.ReturnUrl);
// we can trust Input.ReturnUrl since GetAuthorizationContextAsync returned non-null
return Redirect(Input.ReturnUrl ?? "~/");
}

// request for a local page
Expand All @@ -111,10 +102,10 @@ public async Task<IActionResult> OnPost()
else
{
// user might have clicked on a malicious link - should be logged
throw new Exception("invalid return URL");
throw new ArgumentException("invalid return URL");
}
}

return Page();
}
}
}
Loading
Loading