From 95da83fc2287885007bff04c2a91d1284fedb9ba Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 17 Nov 2023 09:06:22 +0100 Subject: [PATCH 01/16] Update to .NET 8 --- .../GenerateCertiticate.csproj | 4 ++-- api/Api.csproj | 16 ++++++------- identityserver/AspIdUsers.db-shm | Bin 0 -> 32768 bytes identityserver/AspIdUsers.db-wal | Bin 0 -> 24752 bytes identityserver/IdentityServer.csproj | 16 ++++++------- ui/WebCodeFlowPkceClient.csproj | 22 +++++++++--------- 6 files changed, 29 insertions(+), 29 deletions(-) create mode 100644 identityserver/AspIdUsers.db-shm create mode 100644 identityserver/AspIdUsers.db-wal diff --git a/GenerateCertiticate/GenerateCertiticate.csproj b/GenerateCertiticate/GenerateCertiticate.csproj index 180c344..4ffcbd5 100644 --- a/GenerateCertiticate/GenerateCertiticate.csproj +++ b/GenerateCertiticate/GenerateCertiticate.csproj @@ -2,14 +2,14 @@ Exe - net7.0 + net8.0 enable enable - + diff --git a/api/Api.csproj b/api/Api.csproj index 0f5d642..364024d 100644 --- a/api/Api.csproj +++ b/api/Api.csproj @@ -1,23 +1,23 @@  - net7.0 + net8.0 enable true - + - - - - - + + + + + - + diff --git a/identityserver/AspIdUsers.db-shm b/identityserver/AspIdUsers.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..ad5bf914be9ae02c51ed2ebe783e9a1dc520a7bd GIT binary patch literal 32768 zcmeI)u?fOJ6adgO8e1)6X=5j325VPv3KtSvE78&wbOejw40fWIAR(3w%DR+HE9dD`5z`F!!X?(^)&_pwwJW!-lpzQ^@a z-e)n(-pzgez=i+;0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5(jSRe^$ho>_dfi?w()!$KV zc2JuD0RjXF^i^OGMtvRK+yn>^AV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N U0t5&UAV7cs0RjXF5cpex4>(Q1%TUw8+mBitG6$&jSO8kK) z1_owLoHQm5ZchFf9Ne5WQD+^DCT=(woirX20|*I&6YzbLyIh{T;hs<4Tb|rk+&r)- zk&nkE>5#Ot(#fBXzdyb3dg;>iHF&upj^d z2tWV=5P$##AOHafKwwu3EL@2lc6xgiC9%MZjuekeiFim|S@?lQG=qACDKsQr=zBy2 zvaHcS3tU(8+Fp7zoo;(}t1&k@5VqV#^R(Y?MMb-q9~&CT6wQ3$v^82ZD`vDZrfK=B zSNZXgX}?Nm&ydxr4(z-Mlu$=3h z8?X2?t;=SAe`$Qsv`xF5$yTccVHK^+5X}r*RH&*bRGm>8=v4EVPq@%!!a`Lg9#aIN zzUm84aP9_t;bzBSr>~FmwaycY9|Tc%b>eG6Axx+m(V60r!0?#s1z`}1C&@AMw zjJb04Pyc^zY$=d8&GYWWAB%J?ZI3llMolhHG$*V2WU9h6r>)KAMx3j1K3kfYzF2Ea zWt~(>c13Ewmb+5A)XZ5!nYq-Ic~OZ3DuoeB2kok)olV?We0h86U{s4G-$=y;ZR`ZIesS_*5-~R}%zk1cdl(#2W z=K{-8@<(!cXEcttga8B}009U<00Izz00bZa0SG`~8-cFQ-j%6AEZ*6-Iwpvo-;g7? z+x6tfr_a^*$PsKKiQ5o>00bZa0SG_<0uX=z1a^@Cas=yg1o7RIBdCYmW%Zy=xFMn> z1>=Gk_0T20Pr0lFuBwM#w2e392=wLFG`aKc9&!Y`=mSS95P$##AOHafKmY;|fB*y_ xuy+LhGe@xh(Tl>zXAeFgN3eIc3_67X1Rwwb2tWV=5P$##AOL|qBd{q)@EcB=kJA7E literal 0 HcmV?d00001 diff --git a/identityserver/IdentityServer.csproj b/identityserver/IdentityServer.csproj index ed09d8e..a9a9b68 100644 --- a/identityserver/IdentityServer.csproj +++ b/identityserver/IdentityServer.csproj @@ -1,21 +1,21 @@  - net7.0 + net8.0 enable - - + + - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ui/WebCodeFlowPkceClient.csproj b/ui/WebCodeFlowPkceClient.csproj index 156b58b..bab793b 100644 --- a/ui/WebCodeFlowPkceClient.csproj +++ b/ui/WebCodeFlowPkceClient.csproj @@ -1,28 +1,28 @@  - net7.0 + net8.0 enable true - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + + + + - + From 51afdedb89d482a541b3f0543202ee54a077c59b Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 17 Nov 2023 09:09:49 +0100 Subject: [PATCH 02/16] Update claims --- api/HostingExtensions.cs | 4 ++-- identityserver/HostingExtensions.cs | 3 ++- identityserver/IdentityServer.csproj | 2 +- ui/HostingExtensions.cs | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/api/HostingExtensions.cs b/api/HostingExtensions.cs index d77c1c1..3a7b83e 100644 --- a/api/HostingExtensions.cs +++ b/api/HostingExtensions.cs @@ -1,8 +1,8 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Logging; using Microsoft.OpenApi.Models; using Serilog; -using System.IdentityModel.Tokens.Jwt; namespace Api; @@ -15,7 +15,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde var configuration = builder.Configuration; _env = builder.Environment; - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear(); var stsServer = configuration["StsServer"]; diff --git a/identityserver/HostingExtensions.cs b/identityserver/HostingExtensions.cs index bcad2fb..209905a 100644 --- a/identityserver/HostingExtensions.cs +++ b/identityserver/HostingExtensions.cs @@ -3,6 +3,7 @@ using IdentityServer.Models; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Logging; using Serilog; using System.IdentityModel.Tokens.Jwt; @@ -13,7 +14,7 @@ internal static class HostingExtensions { public static WebApplication ConfigureServices(this WebApplicationBuilder builder) { - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear(); builder.Services.AddRazorPages(); diff --git a/identityserver/IdentityServer.csproj b/identityserver/IdentityServer.csproj index a9a9b68..9c26511 100644 --- a/identityserver/IdentityServer.csproj +++ b/identityserver/IdentityServer.csproj @@ -7,7 +7,7 @@ - + diff --git a/ui/HostingExtensions.cs b/ui/HostingExtensions.cs index 75d8738..897e4af 100644 --- a/ui/HostingExtensions.cs +++ b/ui/HostingExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Authentication; +using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; using Serilog; @@ -17,7 +18,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde var configuration = builder.Configuration; _env = builder.Environment; - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.AddAuthentication(options => { From 9f2b7d412d304d92d2a4effa7b5fac6034f75b97 Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 17 Nov 2023 09:12:31 +0100 Subject: [PATCH 03/16] .NET 8 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f7ff2de..19eeaa3 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ ## History +- 2023-11-17 Updated .NET 8 - 2023-11-03 Updated packages, fixed security headers - 2023-10-10 Updated packages - 2023-08-28 Updated packages From ef014e47a9c6dfcd83638b55647a53e65fb8cdb8 Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 17 Nov 2023 09:16:15 +0100 Subject: [PATCH 04/16] update duende --- identityserver/IdentityServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identityserver/IdentityServer.csproj b/identityserver/IdentityServer.csproj index 9c26511..b0c28c8 100644 --- a/identityserver/IdentityServer.csproj +++ b/identityserver/IdentityServer.csproj @@ -6,7 +6,7 @@ - + From bda5a29e2bb9fa044c68c87a1cdf136a7f1148dd Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 17 Nov 2023 09:29:37 +0100 Subject: [PATCH 05/16] remove default mappings --- ui/HostingExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/HostingExtensions.cs b/ui/HostingExtensions.cs index 897e4af..5846a5a 100644 --- a/ui/HostingExtensions.cs +++ b/ui/HostingExtensions.cs @@ -103,6 +103,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde public static WebApplication ConfigurePipeline(this WebApplication app) { IdentityModelEventSource.ShowPII = true; + JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear(); app.UseSerilogRequestLogging(); From b820988c27eac49e05533f5c6adaa4cf35da4472 Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 17 Nov 2023 10:36:03 +0100 Subject: [PATCH 06/16] remove unused code --- ui/HostingExtensions.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/HostingExtensions.cs b/ui/HostingExtensions.cs index 5846a5a..39fba00 100644 --- a/ui/HostingExtensions.cs +++ b/ui/HostingExtensions.cs @@ -18,8 +18,6 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde var configuration = builder.Configuration; _env = builder.Environment; - JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear(); - services.AddAuthentication(options => { options.DefaultScheme = "cookie"; From 6e2e01b215b4ce96c51981a051bb17778b636488 Mon Sep 17 00:00:00 2001 From: damienbod Date: Wed, 13 Dec 2023 13:05:33 +0100 Subject: [PATCH 07/16] update packages --- api/Api.csproj | 2 +- ui/WebCodeFlowPkceClient.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/Api.csproj b/api/Api.csproj index 364024d..832dc0f 100644 --- a/api/Api.csproj +++ b/api/Api.csproj @@ -15,7 +15,7 @@ - + diff --git a/ui/WebCodeFlowPkceClient.csproj b/ui/WebCodeFlowPkceClient.csproj index bab793b..f6d7888 100644 --- a/ui/WebCodeFlowPkceClient.csproj +++ b/ui/WebCodeFlowPkceClient.csproj @@ -20,7 +20,7 @@ - + From ed655f5685d63fb9cf704f344728372d0f56e66c Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 5 Jan 2024 17:03:00 +0100 Subject: [PATCH 08/16] Update nuget packages --- ui/WebCodeFlowPkceClient.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/WebCodeFlowPkceClient.csproj b/ui/WebCodeFlowPkceClient.csproj index f6d7888..2b42ade 100644 --- a/ui/WebCodeFlowPkceClient.csproj +++ b/ui/WebCodeFlowPkceClient.csproj @@ -7,7 +7,7 @@ - + From 43e4a7e310ee63101bfa8827f8884dede60c9a00 Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 5 Jan 2024 17:03:57 +0100 Subject: [PATCH 09/16] readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8269e51..a733c09 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ ## History +- 2024-01-05 Updated packages - 2023-11-17 Updated .NET 8 - 2023-11-03 Updated packages, fixed security headers - 2023-10-10 Updated packages From dd8ad37ae34a0fdcd074fd5988755e1010454ad0 Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 5 Jan 2024 17:05:07 +0100 Subject: [PATCH 10/16] 2024 --- ui/Pages/Shared/_Layout.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/Pages/Shared/_Layout.cshtml b/ui/Pages/Shared/_Layout.cshtml index 530e67a..71b36a0 100644 --- a/ui/Pages/Shared/_Layout.cshtml +++ b/ui/Pages/Shared/_Layout.cshtml @@ -37,7 +37,7 @@
- © 2023 - Web Code Flow PKCE Client using OAuth DPoP + © 2024 - Web Code Flow PKCE Client using OAuth DPoP
From 0f91ef08d2a48b1ae84d5f59224af1f5525badcd Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 5 Jan 2024 17:05:49 +0100 Subject: [PATCH 11/16] 2024 --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 6c85498..babadba 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 damienbod +Copyright (c) 2024 damienbod Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 4e1a38259f6fe9c71571e6f26d477cfb84d174d0 Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 5 Jan 2024 17:06:52 +0100 Subject: [PATCH 12/16] IDP update --- identityserver/IdentityServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identityserver/IdentityServer.csproj b/identityserver/IdentityServer.csproj index b0c28c8..05a57f6 100644 --- a/identityserver/IdentityServer.csproj +++ b/identityserver/IdentityServer.csproj @@ -6,7 +6,7 @@ - + From 5f23c14a89a89350a75c8bd1f265fa1dfcc2af36 Mon Sep 17 00:00:00 2001 From: damienbod Date: Sat, 6 Jan 2024 00:04:51 +0100 Subject: [PATCH 13/16] clean up --- api/HostingExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/HostingExtensions.cs b/api/HostingExtensions.cs index 3a7b83e..3c0e56e 100644 --- a/api/HostingExtensions.cs +++ b/api/HostingExtensions.cs @@ -31,7 +31,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde services.ConfigureDPoPTokensForScheme("dpoptokenscheme"); - builder.Services.AddAuthorization(options => + services.AddAuthorization(options => options.AddPolicy("protectedScope", policy => { policy.RequireClaim("scope", "scope-dpop"); From 3a699f9063532c381823b9f3bcce11694707ae44 Mon Sep 17 00:00:00 2001 From: damienbod Date: Sun, 28 Jan 2024 12:43:07 +0100 Subject: [PATCH 14/16] small updates from Duende samplae --- api/DPoP/ConfigureJwtBearerOptions.cs | 1 + api/DPoP/DPoPExtensions.cs | 6 ++- api/DPoP/DPoPJwtBearerEvents.cs | 12 +++-- api/DPoP/DPoPOptions.cs | 4 +- api/DPoP/DPoPProofValidatonContext.cs | 13 ++++-- api/DPoP/DPoPProofValidatonResult.cs | 18 ++++---- api/DPoP/DPoPProofValidator.cs | 51 +++++++++++++-------- api/DPoP/DPoPServiceCollectionExtensions.cs | 3 ++ api/DPoP/DefaultReplayCache.cs | 4 +- api/DPoP/IReplayCache.cs | 3 ++ 10 files changed, 75 insertions(+), 40 deletions(-) diff --git a/api/DPoP/ConfigureJwtBearerOptions.cs b/api/DPoP/ConfigureJwtBearerOptions.cs index 5327906..f2e36fb 100644 --- a/api/DPoP/ConfigureJwtBearerOptions.cs +++ b/api/DPoP/ConfigureJwtBearerOptions.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Options; +using System; namespace Api; diff --git a/api/DPoP/DPoPExtensions.cs b/api/DPoP/DPoPExtensions.cs index c27acdf..66e3cb2 100644 --- a/api/DPoP/DPoPExtensions.cs +++ b/api/DPoP/DPoPExtensions.cs @@ -1,6 +1,10 @@ using IdentityModel; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; using Microsoft.IdentityModel.Tokens; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text.Json; namespace Api; @@ -18,7 +22,7 @@ public static bool IsDPoPAuthorizationScheme(this HttpRequest request) return authz?.StartsWith(DPoPPrefix, System.StringComparison.Ordinal) == true; } - public static bool TryGetDPoPAccessToken(this HttpRequest request, out string? token) + public static bool TryGetDPoPAccessToken(this HttpRequest request, [NotNullWhen(true)]out string? token) { token = null; diff --git a/api/DPoP/DPoPJwtBearerEvents.cs b/api/DPoP/DPoPJwtBearerEvents.cs index cb38fac..6a959a3 100644 --- a/api/DPoP/DPoPJwtBearerEvents.cs +++ b/api/DPoP/DPoPJwtBearerEvents.cs @@ -1,8 +1,10 @@ using IdentityModel; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; using System.Text; +using System.Threading.Tasks; using static IdentityModel.OidcConstants; namespace Api; @@ -43,6 +45,10 @@ public override async Task TokenValidated(TokenValidatedContext context) if (context.HttpContext.Request.TryGetDPoPAccessToken(out var at)) { var proofToken = context.HttpContext.Request.GetDPoPProofToken(); + if (proofToken == null) + { + throw new InvalidOperationException("Missing DPoP (proof token) HTTP header"); + } var result = await _validator.ValidateAsync(new DPoPProofValidatonContext { Scheme = context.Scheme.Name, @@ -55,7 +61,7 @@ public override async Task TokenValidated(TokenValidatedContext context) if (result.IsError) { // fails the result - context.Fail(result.ErrorDescription ?? result.Error); + context.Fail(result.ErrorDescription ?? result.Error ?? throw new Exception("No ErrorDescription or Error set.")); // we need to stash these values away so they are available later when the Challenge method is called later context.HttpContext.Items["DPoP-Error"] = result.Error; @@ -74,7 +80,7 @@ public override async Task TokenValidated(TokenValidatedContext context) // if the scheme used was not DPoP, then it was Bearer // and if a access token was presented with a cnf, then the // client should have sent it as DPoP, so we fail the request - if (context.Principal!.HasClaim(x => x.Type == JwtClaimTypes.Confirmation)) + if (context.Principal?.HasClaim(x => x.Type == JwtClaimTypes.Confirmation) ?? false) { context.HttpContext.Items["Bearer-ErrorDescription"] = "Must use DPoP when using an access token with a 'cnf' claim"; context.Fail("Must use DPoP when using an access token with a 'cnf' claim"); @@ -129,7 +135,7 @@ public override Task Challenge(JwtBearerChallengeContext context) } } - context.Response.Headers.Add(HeaderNames.WWWAuthenticate, sb.ToString()); + context.Response.Headers.Append(HeaderNames.WWWAuthenticate, sb.ToString()); if (context.HttpContext.Items.ContainsKey("DPoP-Nonce")) diff --git a/api/DPoP/DPoPOptions.cs b/api/DPoP/DPoPOptions.cs index befffa7..3c3ffcb 100644 --- a/api/DPoP/DPoPOptions.cs +++ b/api/DPoP/DPoPOptions.cs @@ -1,4 +1,6 @@ -namespace Api; +using System; + +namespace Api; public class DPoPOptions { diff --git a/api/DPoP/DPoPProofValidatonContext.cs b/api/DPoP/DPoPProofValidatonContext.cs index 0625726..0a73d15 100644 --- a/api/DPoP/DPoPProofValidatonContext.cs +++ b/api/DPoP/DPoPProofValidatonContext.cs @@ -1,3 +1,6 @@ +using System.Collections.Generic; +using System.Security.Claims; + namespace Api; public class DPoPProofValidatonContext @@ -5,25 +8,25 @@ public class DPoPProofValidatonContext /// /// The ASP.NET Core authentication scheme triggering the validation /// - public string Scheme { get; set; } = string.Empty; + public required string Scheme { get; set; } /// /// The HTTP URL to validate /// - public string Url { get; set; } = string.Empty; + public required string Url { get; set; } /// /// The HTTP method to validate /// - public string Method { get; set; } = string.Empty; + public required string Method { get; set; } /// /// The DPoP proof token to validate /// - public string? ProofToken { get; set; } + public required string ProofToken { get; set; } /// /// The access token /// - public string? AccessToken { get; set; } + public required string AccessToken { get; set; } } diff --git a/api/DPoP/DPoPProofValidatonResult.cs b/api/DPoP/DPoPProofValidatonResult.cs index e2295db..2c958a3 100644 --- a/api/DPoP/DPoPProofValidatonResult.cs +++ b/api/DPoP/DPoPProofValidatonResult.cs @@ -2,7 +2,7 @@ namespace Api; public class DPoPProofValidatonResult { - public static DPoPProofValidatonResult Success { get; set; } = new() { IsError = false }; + public static DPoPProofValidatonResult Success = new DPoPProofValidatonResult { IsError = false }; /// /// Indicates if the result was successful or not @@ -12,38 +12,38 @@ public class DPoPProofValidatonResult /// /// The error code for the validation result /// - public string Error { get; set; } = string.Empty; + public string? Error { get; set; } /// /// The error description code for the validation result /// - public string ErrorDescription { get; set; } = string.Empty; + public string? ErrorDescription { get; set; } /// /// The serialized JWK from the validated DPoP proof token. /// - public string JsonWebKey { get; set; } = string.Empty; + public string? JsonWebKey { get; set; } /// /// The JWK thumbprint from the validated DPoP proof token. /// - public string JsonWebKeyThumbprint { get; set; } = string.Empty; + public string? JsonWebKeyThumbprint { get; set; } /// /// The cnf value for the DPoP proof token /// - public string Confirmation { get; set; } = string.Empty; + public string? Confirmation { get; set; } /// /// The payload value of the DPoP proof token. /// - public IDictionary? Payload { get; set; } + public IDictionary? Payload { get; internal set; } /// /// The jti value read from the payload. /// public string? TokenId { get; set; } - + /// /// The ath value read from the payload. /// @@ -62,5 +62,5 @@ public class DPoPProofValidatonResult /// /// The nonce value issued by the server. /// - public string ServerIssuedNonce { get; set; } = string.Empty; + public string? ServerIssuedNonce { get; set; } } diff --git a/api/DPoP/DPoPProofValidator.cs b/api/DPoP/DPoPProofValidator.cs index 3011e21..6b9701f 100644 --- a/api/DPoP/DPoPProofValidator.cs +++ b/api/DPoP/DPoPProofValidator.cs @@ -1,11 +1,16 @@ using IdentityModel; using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.Json; +using System.Threading.Tasks; namespace Api; @@ -125,7 +130,7 @@ protected virtual Task ValidateHeaderAsync(DPoPProofValidatonContext context, DP return Task.CompletedTask; } - if (!token.TryGetHeaderValue>(JwtClaimTypes.JsonWebKey, out var jwkValues)) + if (!token.TryGetHeaderValue(JwtClaimTypes.JsonWebKey, out var jwkValues)) { result.IsError = true; result.ErrorDescription = "Invalid 'jwk' value."; @@ -164,9 +169,9 @@ protected virtual Task ValidateHeaderAsync(DPoPProofValidatonContext context, DP /// /// Validates the signature. /// - protected virtual Task ValidateSignatureAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result) + protected virtual async Task ValidateSignatureAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result) { - TokenValidationResult tokenValidationResult; + TokenValidationResult? tokenValidationResult = null; try { @@ -180,27 +185,26 @@ protected virtual Task ValidateSignatureAsync(DPoPProofValidatonContext context, }; var handler = new JsonWebTokenHandler(); - tokenValidationResult = handler.ValidateToken(context.ProofToken, tvp); + tokenValidationResult = await handler.ValidateTokenAsync(context.ProofToken, tvp); } catch (Exception ex) { Logger.LogDebug("Error parsing DPoP token: {error}", ex.Message); result.IsError = true; result.ErrorDescription = "Invalid signature on DPoP token."; - return Task.CompletedTask; } - if (tokenValidationResult.Exception != null) + if (tokenValidationResult?.Exception != null) { Logger.LogDebug("Error parsing DPoP token: {error}", tokenValidationResult.Exception.Message); result.IsError = true; result.ErrorDescription = "Invalid signature on DPoP token."; - return Task.CompletedTask; } - result.Payload = tokenValidationResult.Claims; - - return Task.CompletedTask; + if (tokenValidationResult != null) + { + result.Payload = tokenValidationResult.Claims; + } } /// @@ -208,7 +212,14 @@ protected virtual Task ValidateSignatureAsync(DPoPProofValidatonContext context, /// protected virtual async Task ValidatePayloadAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result) { - if (result.Payload!.TryGetValue(JwtClaimTypes.DPoPAccessTokenHash, out var ath)) + if(result.Payload is null ) + { + result.IsError = true; + result.ErrorDescription = "Missing payload"; + return; + } + + if (result.Payload.TryGetValue(JwtClaimTypes.DPoPAccessTokenHash, out var ath)) { result.AccessTokenHash = ath as string; } @@ -222,7 +233,7 @@ protected virtual async Task ValidatePayloadAsync(DPoPProofValidatonContext cont using (var sha = SHA256.Create()) { - var bytes = Encoding.UTF8.GetBytes(context.AccessToken!); + var bytes = Encoding.UTF8.GetBytes(context.AccessToken); var hash = sha.ComputeHash(bytes); var accessTokenHash = Base64Url.Encode(hash); @@ -260,15 +271,15 @@ protected virtual async Task ValidatePayloadAsync(DPoPProofValidatonContext cont return; } - if (result.Payload.TryGetValue(JwtClaimTypes.IssuedAt, out object? iat)) + if (result.Payload.TryGetValue(JwtClaimTypes.IssuedAt, out var iat)) { - if (iat is int iatint) + if (iat is int) { - result.IssuedAt = iatint; + result.IssuedAt = (int) iat; } - if (iat is long iatlong) + if (iat is long) { - result.IssuedAt = iatlong; + result.IssuedAt = (long) iat; } } @@ -307,7 +318,7 @@ protected virtual async Task ValidateReplayAsync(DPoPProofValidatonContext conte { var dpopOptions = OptionsMonitor.Get(context.Scheme); - if (await ReplayCache.ExistsAsync(ReplayCachePurpose, result.TokenId!)) + if (await ReplayCache.ExistsAsync(ReplayCachePurpose, result.TokenId!)) // jti is required by an earlier validation { result.IsError = true; result.ErrorDescription = "Detected DPoP proof token replay."; @@ -369,7 +380,7 @@ protected virtual Task ValidateIatAsync(DPoPProofValidatonContext context, DPoPP { var dpopOptions = OptionsMonitor.Get(context.Scheme); - if (IsExpired(context, result, dpopOptions.ClientClockSkew, result.IssuedAt!.Value)) + if (IsExpired(context, result, dpopOptions.ClientClockSkew, result.IssuedAt!.Value)) // iat is required by an earlier validation { result.IsError = true; result.ErrorDescription = "Invalid 'iat' value."; @@ -435,7 +446,7 @@ protected virtual ValueTask GetUnixTimeFromNonceAsync(DPoPProofValidatonCo { try { - var value = DataProtector.Unprotect(result.Nonce!); + var value = DataProtector.Unprotect(result.Nonce!); // nonce is required by an earlier validation if (Int64.TryParse(value, out long iat)) { return ValueTask.FromResult(iat); diff --git a/api/DPoP/DPoPServiceCollectionExtensions.cs b/api/DPoP/DPoPServiceCollectionExtensions.cs index c86fc4d..37304ce 100644 --- a/api/DPoP/DPoPServiceCollectionExtensions.cs +++ b/api/DPoP/DPoPServiceCollectionExtensions.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using System; namespace Api; @@ -15,6 +17,7 @@ public static IServiceCollection ConfigureDPoPTokensForScheme(this IServiceColle services.AddTransient(); services.AddSingleton>(new ConfigureJwtBearerOptions(scheme)); + return services; } diff --git a/api/DPoP/DefaultReplayCache.cs b/api/DPoP/DefaultReplayCache.cs index 43e4a79..9e91309 100644 --- a/api/DPoP/DefaultReplayCache.cs +++ b/api/DPoP/DefaultReplayCache.cs @@ -1,4 +1,6 @@ using Microsoft.Extensions.Caching.Distributed; +using System; +using System.Threading.Tasks; namespace Api; @@ -28,7 +30,7 @@ public async Task AddAsync(string purpose, string handle, DateTimeOffset expirat AbsoluteExpiration = expiration }; - await _cache.SetAsync(Prefix + purpose + handle, Array.Empty(), options); + await _cache.SetAsync(Prefix + purpose + handle, new byte[] { }, options); } /// diff --git a/api/DPoP/IReplayCache.cs b/api/DPoP/IReplayCache.cs index c08018e..4acc4e7 100644 --- a/api/DPoP/IReplayCache.cs +++ b/api/DPoP/IReplayCache.cs @@ -1,3 +1,6 @@ +using System; +using System.Threading.Tasks; + namespace Api; public interface IReplayCache From d9f75c83a11d5c06a208cbdaa9f1ca3c2dfb85a3 Mon Sep 17 00:00:00 2001 From: damienbod Date: Sun, 28 Jan 2024 12:47:16 +0100 Subject: [PATCH 15/16] readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a733c09..cb84761 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ ## History +- 2024-01-28 Updated packages - 2024-01-05 Updated packages - 2023-11-17 Updated .NET 8 - 2023-11-03 Updated packages, fixed security headers From ef2a397b0ed8b416713790a59faed883b1729ef1 Mon Sep 17 00:00:00 2001 From: damienbod Date: Sun, 28 Jan 2024 12:48:07 +0100 Subject: [PATCH 16/16] Update .NET 8 --- api/Api.csproj | 4 ++-- identityserver/AspIdUsers.db-shm | Bin 32768 -> 32768 bytes identityserver/IdentityServer.csproj | 18 +++++++++--------- ...-key-21A1531DB7E7209B391C7E4EF970A18A.json | 1 - ...-key-C2544CD27616B625905B11FFA1772F68.json | 1 + ui/WebCodeFlowPkceClient.csproj | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 identityserver/keys/is-signing-key-21A1531DB7E7209B391C7E4EF970A18A.json create mode 100644 identityserver/keys/is-signing-key-C2544CD27616B625905B11FFA1772F68.json diff --git a/api/Api.csproj b/api/Api.csproj index 832dc0f..48d0090 100644 --- a/api/Api.csproj +++ b/api/Api.csproj @@ -8,11 +8,11 @@ - + - + diff --git a/identityserver/AspIdUsers.db-shm b/identityserver/AspIdUsers.db-shm index ad5bf914be9ae02c51ed2ebe783e9a1dc520a7bd..5024be8fecda54a8fb6f56a880c017e6c4604fcc 100644 GIT binary patch delta 55 ucmZo@U}|V!;*@x#%K!!wIpqZ-4HVUOon2&rEXWELoEWRj$hNVuz8(N>WDVZ{ delta 55 vcmZo@U}|V!;*@x#%K!pQ6FKDtiwzXjo}67|fGo%g7MvKX%*e2@vA!MvdT - - - - + + + + - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/identityserver/keys/is-signing-key-21A1531DB7E7209B391C7E4EF970A18A.json b/identityserver/keys/is-signing-key-21A1531DB7E7209B391C7E4EF970A18A.json deleted file mode 100644 index cbfbcad..0000000 --- a/identityserver/keys/is-signing-key-21A1531DB7E7209B391C7E4EF970A18A.json +++ /dev/null @@ -1 +0,0 @@ -{"Version":1,"Id":"21A1531DB7E7209B391C7E4EF970A18A","Created":"2023-08-08T11:05:55.3464982Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8O32kMVNdJZLqq7VNXR93_NxKfR9PM_o-CyjT41KWKAgXGtWCo8kSwwNjB-bQJ1-mzJ73HaG-JJfczwjArN_opjBGkqgpQhAVLGhu3EEkLE0In1njplCAvzmseRAN1PMlK8jWOwPmclDNl5Wpe3B6OJIP3rZnzVWLuA9nDMXqDQOPZNAnBbUrbAVOiId3bkA9fC_2R9bDMXsNcG-xJYrMYtQZSsjFTOw2ZrznJwNQFcR7fvEwqXsTuTXvMTXdwUzQ7MTjFwXtu8-elxBYDu7AlEU5O5knks8vr0Ynxz7Ceb3FxU9dJ-La3mUKKNipkcCYS_Hf4b27K7I4BQ-m7TLfjKwhvYmd9a0QvLTFygPdDNfYG9Iv6tKvImRD3ZQ6oKN_pIR2Em7kkKzGJLCNPzh2D8Wvdh5Iz0U9w1g7ILAwX1DTHy4acaEyxP0ivnUqCEX6JPRbV15mZ6yXrzvT3hdbp9XNuKELm8iDGHfXZ0Qu2NhzN08ItrR7VvQ2KS1MdHGXA-A5oSiqLfLWSVunZ-J7PYst2CGi8Xw49BjjN_Lq1IdSfN5w-6kv_jY-1JMsYE0jt1ZRYZRl6CqxH5EdtmZrMRzgpYKwqeRDI5jGjEMHU-nRTbq05LP_NulTXJpfUTGvou6i1srnb6panPYe47Z3VLQaQ6mo3TS6VJ9B7yAvGwWGllEMGooQkhCz-nQ_gb1JRN-VlSOYv0I1dz5KJlHjQKqAwY8UlLD2NVh1IJm3CbF6eL6xtnnHiEyGOJaEkhLkpMCfdFX5BR6vyh9xazSKCZqo0fXWJ5PPFfkcDerxZ3_pUCatjxJWVItua702y9X7HTkHoQ-bRDXUsN2NLV6IIE7wpR_-EqoMrewIh1_z6k-JrsZxq7byczYX7k81Yt0_Oag55NE0Mj_oHDaDVd3V_MTDBNNSw0jJfv8gPU612lEh7WiKqSju0bQjr9nI9b7PvgLga1vzqFq-SINMdXhdIBO4vdcVw9fv6ieuiEeJOSBkyLhSOjjppczpzNQR6Zrpw7RDjPHfgKFjpSnlIf1rZWADSpPg5aTR2yp_09A0DWfzOp5DnMF0JESeuSqBH7N5iy8LJZOcjm_frsSZ5jD0sZh4zy6FrD9TIK6HRhZoCxk13YlYiNzAe1B3H6RqHsFhm3lu7Zk1eI2EPJzUm9AqRnt5vSG8oclrlqpPnYhS01MmQKIrEfdDK2j70ixcXnhJEX6z4sV0oKpCRswm_nZzILdcSzsLtyyFX65__7rg19xkR0s7NJ41h4BtONEQpDPqLSt0pso9bRc2BMQCgjw3lia9ZkiVumc38Pkog5uiOIwN4BFeA6kfPRsaqLeHfFOJKtTho3HhaQYOQxUY5jtyIFnDC-1_t2UGSNIe7-M7fsW7ljcOS6ZEgGo1AJxDvAnV0PuyX0JMJfjKt_3vNY_RsA8ESWmnUQjygMdD1WK6OwDt7gaAF7GbyLMRGQ7yaU9RCNbkTXYs_durJKfdTt_RcAlTLEKg0xEsyuO7pXr-zW9cqvnutPJBMmDiwzMNCJpkCnePj0yt_G8rLabKG5OuhciPV8kjWwGAKaJWEM1Vkr0usO41qrqETDtc4PmENEZlcUYSquRmJ-oCBA-o9W2Xomv62-Fke5j18gqzemlN2tru2SeElPC9PW09nDkk3eP6OdEaAYX9rF7IBW1qLCiaf_zaEluL5aHpIB-1zyY_Hi8YyyJc5Yryt-GNc-GQ03F8JJkhQOBfnsZgx0F5_1CKoQjKd7wPUF1C_NaTJKcuwJfUmdIOO8mZrTXSuS30RJE6K50uf-b_ITIRXP6YY9cicyN71SvR_sX4IEV4OcoT0T2-S67wt1fPDdGux15dNeEiaaSteUTG522bEWSpvgEvXK-exy_oEvGkaqdtTLomdksGBwaj39GWLSKRlm9LgoY_GuAYt9FjqGxef5gCWBpLI_rct5YlV7Ha3hUvytZCHb0a7YvF_C70VsJOWSY4pGzLP4NSUs8DDHG4R9SSUewykCw_-VYRiTSj14pntJ5svn2vkb79ckcjcVb2Rh8lOFF5eAt_gyPuIgQnZpP0omOG29qstaioRN0zqyPaK2xtgsCoqU4hqNNS81IDEKVSXDvqLKoGhBxeRy6rm2CQDvdBtAh3JXm6OT0vuvOZLevQoCgSj5UPHrVRV2uqcOEUzFGiT_mJKExbz-PViHsQXGKM_jjhFsxzbvex8QoTF4kcqSe0GvX_VelMIE0deVx8qSMJJO3LtIDo1FZJfRnK638sCSYbiBZqFQfVopae9VkqAyFwW014_pQIXJXBf7gI1fGi2qMAjX_1vcRsS0EXWTNcydXgbPMa_k0vDfdv-6BWB-y4tdXxUQP5eL0QRMFRrl-xdqvJJ76jmqUzMBk_HcFich2rtmBC-4WKmdJafj9-Fom0biFIQcCPHMuWqbGw7cDvV7gpdNO02d3_16JLpgGQljdqOgqsFvFSisxWpxhsmm4","DataProtected":true} \ No newline at end of file diff --git a/identityserver/keys/is-signing-key-C2544CD27616B625905B11FFA1772F68.json b/identityserver/keys/is-signing-key-C2544CD27616B625905B11FFA1772F68.json new file mode 100644 index 0000000..be49596 --- /dev/null +++ b/identityserver/keys/is-signing-key-C2544CD27616B625905B11FFA1772F68.json @@ -0,0 +1 @@ +{"Version":1,"Id":"C2544CD27616B625905B11FFA1772F68","Created":"2024-01-05T23:12:48.4953516Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8M0N7isXFh9CvJyBCGg-Idazzhor3_JIZaevUYDJrRf0Y-ta7w3BrvjuNgfHIEzWyahwWOKtPmyAukpPXdiNcCuyXXGQzEIR6DdmfthQ62z9C6UzstQ87o0smhBqj-CMtOCdav1hKZ9OpTI5PPM1HzDCkUSijnt1R16YloHHPnqAD8aYTYg0fprMsusdrEuKGZsDhGvq0VERM_8w4Ewm2R8IGjH6UN9y75q480NKBgdKNdgpeHklBjYZd0uUPG3JcO_n7AIYzmc5t4r6TjdWUHHarVcjS9irknv5Vl5fsLUM8lqHW6oYegBxsI9obPSTBET0p7IdgdaIVY0Dd7yyKtYk09SbHB8q8EvJkZSIpxVTaCJBVg8MJjJKkmd2Yjqz7jwdG6g9TIXudzB5tWdGop2r63mLQdNsWaZg89aMvEJjuVfmFqYoIpy6FWjVVJxYjhbGTYf_olxK3ZhPWKPP2EEVMRyk0HErxma4a8_bUFTmeqAtmZHTgY0OzA4MtYqSuIfZvAu0osjpsSeyChAylqUjd_HImwCAVelYkXCPY-jGSUjGVMGY4VrETw8EGIBBp90oMrMy4G_wTk8ud4SEG1UbV4LemlDoPtGGraZa2OKzHNO84PWosgHnozAeYHFkb92B1R6QtXniIpht6BCKMVhw6H9LYpKOC51Up_mbYARwQ-Rc7_HK-1uPrKBiw3aUckXDoTi07EdFMh6w1iAcW8WnaYYToe7BxCMSfVo-JWOeoYb69efh6GBwXbqoMA9CZoMcqhGG9-N_AQReWhwpR_6LNv9NFtC23O6wiuW-VKkdb7PqWK5uPI0_6k2yNVROj568kJvNe7lxZbw94lUAL7w1RaDd-hKrb9miXa6ONk19EkDXp_ZtOlnGh_JBOUsQZMGnQca9ZEWhrz2RKoJvNG7JfQdaFVAumezjvcL60hhLCIOXs87SON4JCiuxwSYvVHJRsYoeb5RwKF8ncccugNJWKut2rYqLGEyby_xNDexTDXREUrgeLRv_G0CjjNjCaUTWMl0nIJct7CWHEIEglG-aYMT4u7DjeWx36-y-526n7_9jqPwaoG0mJm3hoRa8EaXrp56_hDRUIR1S_uAcB3o9QuiX0_hM3QXcBMphOjZrOYC9HPqucumcv_Nt41m_usHl_LIpgAkcMubjtGqZflf1RzzZ8J1Xr84K0be5MdeyaPRnhjXBk5kiPKwzk8tP8WuiS_5qSkSHBehFmWdJvAiFWBk83zefkPGQBIq3q1208ePVgI639dLlFLILvWacQNpZNRFSD91j67LSEURNWwHWtMd_CQoYAQFTnNB7y0t55-fdZ6Dy8yQzqZ88qrZIq3PTlvXn15slkLW8v-vhYenw9g2rB8YtQyBdocImptIN53Qenfn0FFZ6lwB89bmaDwBBcApGIPZ4bFe8Qf-hC1l5B-eF646ZSN9_tJr6i9pWtf_ATzo8pomzCKFEywVEcYC76gxyNNLWOKRM-Rt0b4SHSElppXqsTpMXN-ObR_OCMgGlDPDhxVOx1GjQ41qJVPX-tP0Iej9jCoGAxnwopUXGSASWQsg64QysUpjE0GIoR4aRMZyN-5EgR0Kns6VIqdAJKc79d9xk7w7IC3Qfg95OnIpKnbB6Xxoh4cue_4dK8pecNo9xLglm7gEA7WSw--WdCeC13hA_R1-bZvSzyvNF6nyafD8oyeXy4za2Fi55qUNfcvQxrrU9fRpU8cqLJS6baE-qzslz2sJdWTkai7LcefJfvgVbHoEmo1VAFlWiTI5RFXZp4xhRuEog7TAER2SG55iZ4lb2nrr3Sq9uQsZLRD0x1RwGz6O_mVwaiXaMdGD-sliioh86tfQvtDq1HxiIBTMnaGGdTtWwpy97KUXY-eOIUhpDpi-pY4ANRWATbkckvMCrxx9EQZPomFXVc9Qml4Ka-C_xFKlnwW_Z8Srzc7AD-0I2EejzFnN0p5RbQdSyWgQpV0ebdc3O_ZYu-n27NG-nNMUQ4Bqrcg9A55Kgw5r3BQmdZnL2mgnIHNgitj6nQq61l0mmK3wl2ef34agBWJIH8Yc751ld2E6ZEuB7Q4tlYkTaVRpx89IIMtB3VqekA6ldRKeoNJW_bFpY-DbaTGfjpVXVzkB_EU8OEOwxtm3wWkxLnmraemTWiiLhWdFNwHFk08hWwyIGWcvK6XUE4aK-fJ-1sL5SyYRPnyL1CupRMUTI3vKjJMKAKe7kpbJq4emZMNzAarw3SlOx1QdTx5EdGa4Goo5lRaUhP2xcC_PEh9poAUzz_jOyNANFD-NXJhsHYo03anuG33EXyr28rOtowei8-p3jII-DLOVqiY0DhoOBzc79CR1vrZB8YoPbVJyl4OZ8HJD427SyuEaQD6GujOuZAwqmow9hAse7AZCJlzr8nRz7iBP_vgbbXQHjd_6VGEVYeQntn2-Y0Q6uKscaUldbbs799F6ofR7dnrV6pbdcIQAqMvm0aIc2","DataProtected":true} \ No newline at end of file diff --git a/ui/WebCodeFlowPkceClient.csproj b/ui/WebCodeFlowPkceClient.csproj index 2b42ade..8dafc45 100644 --- a/ui/WebCodeFlowPkceClient.csproj +++ b/ui/WebCodeFlowPkceClient.csproj @@ -8,17 +8,17 @@ - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - +