From 7f42ee34dbf4c9d2e07df3e8eb8b9dd8f52eed24 Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Fri, 16 Feb 2024 20:28:04 -0600 Subject: [PATCH 1/7] OidcClient - update IdentityModel dependency --- src/OidcClient/OidcClient.csproj | 2 +- test/OidcClient.Tests/Infrastructure/NetworkHandler.cs | 5 +++-- test/OidcClient.Tests/OidcClient.Tests.csproj | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/OidcClient/OidcClient.csproj b/src/OidcClient/OidcClient.csproj index 6c4da54..732aac3 100644 --- a/src/OidcClient/OidcClient.csproj +++ b/src/OidcClient/OidcClient.csproj @@ -40,7 +40,7 @@ - + diff --git a/test/OidcClient.Tests/Infrastructure/NetworkHandler.cs b/test/OidcClient.Tests/Infrastructure/NetworkHandler.cs index 5b4b540..67537f4 100644 --- a/test/OidcClient.Tests/Infrastructure/NetworkHandler.cs +++ b/test/OidcClient.Tests/Infrastructure/NetworkHandler.cs @@ -5,6 +5,7 @@ using System; using System.Net; using System.Net.Http; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -86,11 +87,11 @@ protected override async Task SendAsync(HttpRequestMessage { if (_selector != null) { - response.Content = new StringContent(_selector(request)); + response.Content = new StringContent(_selector(request), Encoding.UTF8, "application/json"); } else { - response.Content = new StringContent(_document); + response.Content = new StringContent(_document, Encoding.UTF8, "application/json"); } } diff --git a/test/OidcClient.Tests/OidcClient.Tests.csproj b/test/OidcClient.Tests/OidcClient.Tests.csproj index aaa989c..5dca8c5 100644 --- a/test/OidcClient.Tests/OidcClient.Tests.csproj +++ b/test/OidcClient.Tests/OidcClient.Tests.csproj @@ -16,7 +16,7 @@ - + From ef8815bb83e3c7cf8c09b20f6fcc3f98ff24c9e0 Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Fri, 16 Feb 2024 20:38:00 -0600 Subject: [PATCH 2/7] DPoP - Update IdentityModel, wilson dependencies - Pass a Dictionary instead of an anonymous type for jwk claims (needed due to changes in jwt handler) - replace deprecated IHeaderDictionary.Add with Append - replace deprecated ValidateToken with ValidateTokenAsync - tests need to use IdentityServer 7 to get its changes to support new wilson library. This forces us to drop net6.0 and net7.0 from the target frameworks of the dpop test project --- src/DPoP/DPoP.csproj | 4 ++-- src/DPoP/DPoPProofTokenFactory.cs | 18 +++++++++--------- test/DPoPTests/DPoPTests.csproj | 8 ++++---- .../Framework/DPoP/DPoPJwtBearerEvents.cs | 3 ++- .../Framework/DPoP/DPoPProofValidator.cs | 12 ++++++------ 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/DPoP/DPoP.csproj b/src/DPoP/DPoP.csproj index a0fe34d..556f912 100644 --- a/src/DPoP/DPoP.csproj +++ b/src/DPoP/DPoP.csproj @@ -39,9 +39,9 @@ - + - + diff --git a/src/DPoP/DPoPProofTokenFactory.cs b/src/DPoP/DPoPProofTokenFactory.cs index 3a6a4b7..fb33818 100644 --- a/src/DPoP/DPoPProofTokenFactory.cs +++ b/src/DPoP/DPoPProofTokenFactory.cs @@ -43,21 +43,21 @@ public DPoPProof CreateProofToken(DPoPProofRequest request) object jwk; if (string.Equals(jsonWebKey.Kty, JsonWebAlgorithmsKeyTypes.EllipticCurve)) { - jwk = new + jwk = new Dictionary { - kty = jsonWebKey.Kty, - x = jsonWebKey.X, - y = jsonWebKey.Y, - crv = jsonWebKey.Crv + { "kty", jsonWebKey.Kty }, + { "x", jsonWebKey.X }, + { "y", jsonWebKey.Y }, + { "crv", jsonWebKey.Crv } }; } else if (string.Equals(jsonWebKey.Kty, JsonWebAlgorithmsKeyTypes.RSA)) { - jwk = new + jwk = new Dictionary { - kty = jsonWebKey.Kty, - e = jsonWebKey.E, - n = jsonWebKey.N + { "kty", jsonWebKey.Kty }, + { "e", jsonWebKey.E }, + { "n", jsonWebKey.N } }; } else diff --git a/test/DPoPTests/DPoPTests.csproj b/test/DPoPTests/DPoPTests.csproj index ce0c530..56e309a 100644 --- a/test/DPoPTests/DPoPTests.csproj +++ b/test/DPoPTests/DPoPTests.csproj @@ -1,7 +1,7 @@  - net6.0;net7.0;net8.0 + net8.0 @@ -17,9 +17,9 @@ - - - + + + diff --git a/test/DPoPTests/Framework/DPoP/DPoPJwtBearerEvents.cs b/test/DPoPTests/Framework/DPoP/DPoPJwtBearerEvents.cs index 64bb836..921f9d2 100644 --- a/test/DPoPTests/Framework/DPoP/DPoPJwtBearerEvents.cs +++ b/test/DPoPTests/Framework/DPoP/DPoPJwtBearerEvents.cs @@ -1,5 +1,6 @@ using IdentityModel; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; using System.Text; @@ -130,7 +131,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/test/DPoPTests/Framework/DPoP/DPoPProofValidator.cs b/test/DPoPTests/Framework/DPoP/DPoPProofValidator.cs index 606a0ea..2ecb8a2 100644 --- a/test/DPoPTests/Framework/DPoP/DPoPProofValidator.cs +++ b/test/DPoPTests/Framework/DPoP/DPoPProofValidator.cs @@ -130,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."; @@ -169,7 +169,7 @@ 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; @@ -185,14 +185,14 @@ 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; + return; } if (tokenValidationResult.Exception != null) @@ -200,12 +200,12 @@ protected virtual Task ValidateSignatureAsync(DPoPProofValidatonContext context, Logger.LogDebug("Error parsing DPoP token: {error}", tokenValidationResult.Exception.Message); result.IsError = true; result.ErrorDescription = "Invalid signature on DPoP token."; - return Task.CompletedTask; + return; } result.Payload = tokenValidationResult.Claims; - return Task.CompletedTask; + return; } /// From f54334329414526bd5d1df9dd14690554b6b12ab Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Fri, 16 Feb 2024 20:38:48 -0600 Subject: [PATCH 3/7] IdentityTokenValidator - update wilson dependency --- src/IdentityTokenValidator/IdentityTokenValidator.csproj | 2 +- .../JwtHandlerIdentityTokenValidator.cs | 6 +++--- test/JwtValidationTests/Infrastructure/Crypto.cs | 2 +- test/JwtValidationTests/Infrastructure/NetworkHandler.cs | 5 +++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/IdentityTokenValidator/IdentityTokenValidator.csproj b/src/IdentityTokenValidator/IdentityTokenValidator.csproj index 5f0489f..de38803 100644 --- a/src/IdentityTokenValidator/IdentityTokenValidator.csproj +++ b/src/IdentityTokenValidator/IdentityTokenValidator.csproj @@ -36,7 +36,7 @@ - + diff --git a/src/IdentityTokenValidator/JwtHandlerIdentityTokenValidator.cs b/src/IdentityTokenValidator/JwtHandlerIdentityTokenValidator.cs index e160abf..1c03793 100644 --- a/src/IdentityTokenValidator/JwtHandlerIdentityTokenValidator.cs +++ b/src/IdentityTokenValidator/JwtHandlerIdentityTokenValidator.cs @@ -81,7 +81,7 @@ public async Task ValidateAsync(string identityTo }; } - var result = ValidateSignature(identityToken, handler, parameters, options, logger); + var result = await ValidateSignatureAsync(identityToken, handler, parameters, options, logger); if (result.IsValid == false) { if (result.Exception is SecurityTokenSignatureKeyNotFoundException) @@ -123,7 +123,7 @@ public async Task ValidateAsync(string identityTo }; } - private TokenValidationResult ValidateSignature(string identityToken, JsonWebTokenHandler handler, TokenValidationParameters parameters, OidcClientOptions options, ILogger logger) + private async Task ValidateSignatureAsync(string identityToken, JsonWebTokenHandler handler, TokenValidationParameters parameters, OidcClientOptions options, ILogger logger) { if (parameters.RequireSignedTokens) { @@ -174,7 +174,7 @@ private TokenValidationResult ValidateSignature(string identityToken, JsonWebTok parameters.IssuerSigningKeys = keys; } - return handler.ValidateToken(identityToken, parameters); + return await handler.ValidateTokenAsync(identityToken, parameters); } private static string CheckRequiredClaim(ClaimsPrincipal user) diff --git a/test/JwtValidationTests/Infrastructure/Crypto.cs b/test/JwtValidationTests/Infrastructure/Crypto.cs index 282ca2c..72a28c6 100644 --- a/test/JwtValidationTests/Infrastructure/Crypto.cs +++ b/test/JwtValidationTests/Infrastructure/Crypto.cs @@ -78,7 +78,7 @@ public static IdentityModel.Jwk.JsonWebKeySet CreateKeySet(RsaSecurityKey key) public static string CreateJwt(RsaSecurityKey key, string issuer, string audience, params Claim[] claims) { var jwtClaims = new List(claims); - jwtClaims.Add(new Claim(JwtClaimTypes.IssuedAt, "now")); + jwtClaims.Add(new Claim(JwtClaimTypes.IssuedAt, DateTime.UtcNow.Ticks.ToString(), ClaimValueTypes.Integer64)); SigningCredentials credentials = null; if (key != null) diff --git a/test/JwtValidationTests/Infrastructure/NetworkHandler.cs b/test/JwtValidationTests/Infrastructure/NetworkHandler.cs index 5b4b540..67537f4 100644 --- a/test/JwtValidationTests/Infrastructure/NetworkHandler.cs +++ b/test/JwtValidationTests/Infrastructure/NetworkHandler.cs @@ -5,6 +5,7 @@ using System; using System.Net; using System.Net.Http; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -86,11 +87,11 @@ protected override async Task SendAsync(HttpRequestMessage { if (_selector != null) { - response.Content = new StringContent(_selector(request)); + response.Content = new StringContent(_selector(request), Encoding.UTF8, "application/json"); } else { - response.Content = new StringContent(_document); + response.Content = new StringContent(_document, Encoding.UTF8, "application/json"); } } From 29cfe342a47732c442daa50c953765bb34888f0a Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Thu, 22 Feb 2024 08:56:38 -0600 Subject: [PATCH 4/7] Update IdentityModel to 7.0.0-preview.3 --- src/DPoP/DPoP.csproj | 2 +- src/OidcClient/OidcClient.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DPoP/DPoP.csproj b/src/DPoP/DPoP.csproj index 556f912..73002b6 100644 --- a/src/DPoP/DPoP.csproj +++ b/src/DPoP/DPoP.csproj @@ -39,7 +39,7 @@ - + diff --git a/src/OidcClient/OidcClient.csproj b/src/OidcClient/OidcClient.csproj index 732aac3..aadd971 100644 --- a/src/OidcClient/OidcClient.csproj +++ b/src/OidcClient/OidcClient.csproj @@ -40,7 +40,7 @@ - + From 3c7158876cfd4123007f72e1f851b64894bfdda5 Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Fri, 23 Feb 2024 15:22:08 -0600 Subject: [PATCH 5/7] Enable trimming in IdentityTokenValidator --- src/IdentityTokenValidator/IdentityTokenValidator.csproj | 3 +++ test/TrimmableAnalysis/TrimmableAnalysis.csproj | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/IdentityTokenValidator/IdentityTokenValidator.csproj b/src/IdentityTokenValidator/IdentityTokenValidator.csproj index de38803..3e165b0 100644 --- a/src/IdentityTokenValidator/IdentityTokenValidator.csproj +++ b/src/IdentityTokenValidator/IdentityTokenValidator.csproj @@ -22,6 +22,9 @@ embedded + + true + True ../../key.snk diff --git a/test/TrimmableAnalysis/TrimmableAnalysis.csproj b/test/TrimmableAnalysis/TrimmableAnalysis.csproj index ea08cd2..f60b2d8 100644 --- a/test/TrimmableAnalysis/TrimmableAnalysis.csproj +++ b/test/TrimmableAnalysis/TrimmableAnalysis.csproj @@ -12,6 +12,9 @@ + + + From ad33a1e4577cc6a5f198c8aefe803971b9da530a Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Fri, 23 Feb 2024 19:40:52 -0600 Subject: [PATCH 6/7] Support trimming in OidcClient.DPoP --- src/DPoP/DPoP.csproj | 3 ++ src/DPoP/DPoPProof.cs | 1 - src/DPoP/DPoPProofPayload.cs | 28 +++++++++++++++++++ src/DPoP/DPoPProofTokenFactory.cs | 18 ++++++------ src/DPoP/JsonWebKeys.cs | 4 +-- src/DPoP/SourceGenerationContext.cs | 16 +++++++++++ .../TrimmableAnalysis.csproj | 6 ++++ 7 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 src/DPoP/DPoPProofPayload.cs create mode 100644 src/DPoP/SourceGenerationContext.cs diff --git a/src/DPoP/DPoP.csproj b/src/DPoP/DPoP.csproj index 73002b6..4f8cefb 100644 --- a/src/DPoP/DPoP.csproj +++ b/src/DPoP/DPoP.csproj @@ -26,6 +26,9 @@ embedded + + true + True ../../key.snk diff --git a/src/DPoP/DPoPProof.cs b/src/DPoP/DPoPProof.cs index f611fc3..e14842d 100644 --- a/src/DPoP/DPoPProof.cs +++ b/src/DPoP/DPoPProof.cs @@ -1,7 +1,6 @@ // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - namespace IdentityModel.OidcClient.DPoP; /// diff --git a/src/DPoP/DPoPProofPayload.cs b/src/DPoP/DPoPProofPayload.cs new file mode 100644 index 0000000..5a432b4 --- /dev/null +++ b/src/DPoP/DPoPProofPayload.cs @@ -0,0 +1,28 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Text.Json.Serialization; + +namespace IdentityModel.OidcClient.DPoP; + +/// +/// Internal class to aid serialization of DPoP proof token payloads. Giving +/// each claim a property allows us to add this type to the source generated +/// serialization +/// +internal class DPoPProofPayload +{ + [JsonPropertyName(JwtClaimTypes.JwtId)] + internal string JwtId { get; set; } = default!; + [JsonPropertyName(JwtClaimTypes.DPoPHttpMethod)] + internal string DPoPHttpMethod { get; set; } = default!; + [JsonPropertyName(JwtClaimTypes.DPoPHttpUrl)] + internal string DPoPHttpUrl { get; set; } = default!; + [JsonPropertyName(JwtClaimTypes.IssuedAt)] + internal long IssuedAt { get; set; } + [JsonPropertyName(JwtClaimTypes. DPoPAccessTokenHash)] + internal string? DPoPAccessTokenHash { get; set; } + [JsonPropertyName(JwtClaimTypes. Nonce)] + internal string? Nonce { get; set; } +} \ No newline at end of file diff --git a/src/DPoP/DPoPProofTokenFactory.cs b/src/DPoP/DPoPProofTokenFactory.cs index fb33818..170f83d 100644 --- a/src/DPoP/DPoPProofTokenFactory.cs +++ b/src/DPoP/DPoPProofTokenFactory.cs @@ -40,7 +40,7 @@ public DPoPProof CreateProofToken(DPoPProofRequest request) // jwk: representing the public key chosen by the client, in JSON Web Key (JWK) [RFC7517] format, // as defined in Section 4.1.3 of [RFC7515]. MUST NOT contain a private key. - object jwk; + Dictionary jwk; if (string.Equals(jsonWebKey.Kty, JsonWebAlgorithmsKeyTypes.EllipticCurve)) { jwk = new Dictionary @@ -71,12 +71,12 @@ public DPoPProof CreateProofToken(DPoPProofRequest request) { JwtClaimTypes.JsonWebKey, jwk }, }; - var payload = new Dictionary + var payload = new DPoPProofPayload { - { JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId() }, - { JwtClaimTypes.DPoPHttpMethod, request.Method }, - { JwtClaimTypes.DPoPHttpUrl, request.Url }, - { JwtClaimTypes.IssuedAt, DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, + JwtId = CryptoRandom.CreateUniqueId(), + DPoPHttpMethod = request.Method, + DPoPHttpUrl = request.Url, + IssuedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds() }; if (!string.IsNullOrWhiteSpace(request.AccessToken)) @@ -87,17 +87,17 @@ public DPoPProof CreateProofToken(DPoPProofRequest request) var hash = sha256.ComputeHash(Encoding.ASCII.GetBytes(request.AccessToken)); var ath = Base64Url.Encode(hash); - payload.Add(JwtClaimTypes.DPoPAccessTokenHash, ath); + payload.DPoPAccessTokenHash = ath; } if (!string.IsNullOrEmpty(request.DPoPNonce)) { - payload.Add(JwtClaimTypes.Nonce, request.DPoPNonce!); + payload.Nonce = request.DPoPNonce!; } var handler = new JsonWebTokenHandler() { SetDefaultTimesOnTokenCreation = false }; var key = new SigningCredentials(jsonWebKey, jsonWebKey.Alg); - var proofToken = handler.CreateToken(JsonSerializer.Serialize(payload), key, header); + var proofToken = handler.CreateToken(JsonSerializer.Serialize(payload, SourceGenerationContext.Default.DPoPProofPayload), key, header); return new DPoPProof { ProofToken = proofToken! }; } diff --git a/src/DPoP/JsonWebKeys.cs b/src/DPoP/JsonWebKeys.cs index bee3aac..17583f6 100644 --- a/src/DPoP/JsonWebKeys.cs +++ b/src/DPoP/JsonWebKeys.cs @@ -31,7 +31,7 @@ public static JsonWebKey CreateRsa(string algorithm = OidcConstants.Algorithms.A /// public static string CreateRsaJson(string algorithm = OidcConstants.Algorithms.Asymmetric.PS256) { - return JsonSerializer.Serialize(CreateRsa(algorithm)); + return JsonSerializer.Serialize(CreateRsa(algorithm), SourceGenerationContext.Default.JsonWebKey); } /// @@ -53,7 +53,7 @@ public static JsonWebKey CreateECDsa(string algorithm = OidcConstants.Algorithms /// public static string CreateECDsaJson(string algorithm = OidcConstants.Algorithms.Asymmetric.ES256) { - return JsonSerializer.Serialize(CreateECDsa(algorithm)); + return JsonSerializer.Serialize(CreateECDsa(algorithm), SourceGenerationContext.Default.JsonWebKey); } internal static string GetCurveNameFromSigningAlgorithm(string alg) diff --git a/src/DPoP/SourceGenerationContext.cs b/src/DPoP/SourceGenerationContext.cs new file mode 100644 index 0000000..bc3bc48 --- /dev/null +++ b/src/DPoP/SourceGenerationContext.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; +using Microsoft.IdentityModel.Tokens; + +namespace IdentityModel.OidcClient.DPoP +{ + [JsonSourceGenerationOptions( + WriteIndented = false, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + GenerationMode = JsonSourceGenerationMode.Metadata, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + [JsonSerializable(typeof(JsonWebKey))] + [JsonSerializable(typeof(DPoPProofPayload))] + internal partial class SourceGenerationContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/test/TrimmableAnalysis/TrimmableAnalysis.csproj b/test/TrimmableAnalysis/TrimmableAnalysis.csproj index f60b2d8..248e2a7 100644 --- a/test/TrimmableAnalysis/TrimmableAnalysis.csproj +++ b/test/TrimmableAnalysis/TrimmableAnalysis.csproj @@ -15,6 +15,12 @@ + + + + + + From 1dc50d87cb2d6fb4b741a3e10ffede1f64769eef Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Fri, 23 Feb 2024 19:49:41 -0600 Subject: [PATCH 7/7] Fix tests --- src/DPoP/DPoPProofPayload.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DPoP/DPoPProofPayload.cs b/src/DPoP/DPoPProofPayload.cs index 5a432b4..4e6752a 100644 --- a/src/DPoP/DPoPProofPayload.cs +++ b/src/DPoP/DPoPProofPayload.cs @@ -14,15 +14,15 @@ namespace IdentityModel.OidcClient.DPoP; internal class DPoPProofPayload { [JsonPropertyName(JwtClaimTypes.JwtId)] - internal string JwtId { get; set; } = default!; + public string JwtId { get; set; } = default!; [JsonPropertyName(JwtClaimTypes.DPoPHttpMethod)] - internal string DPoPHttpMethod { get; set; } = default!; + public string DPoPHttpMethod { get; set; } = default!; [JsonPropertyName(JwtClaimTypes.DPoPHttpUrl)] - internal string DPoPHttpUrl { get; set; } = default!; + public string DPoPHttpUrl { get; set; } = default!; [JsonPropertyName(JwtClaimTypes.IssuedAt)] - internal long IssuedAt { get; set; } + public long IssuedAt { get; set; } [JsonPropertyName(JwtClaimTypes. DPoPAccessTokenHash)] - internal string? DPoPAccessTokenHash { get; set; } + public string? DPoPAccessTokenHash { get; set; } [JsonPropertyName(JwtClaimTypes. Nonce)] - internal string? Nonce { get; set; } + public string? Nonce { get; set; } } \ No newline at end of file