From 0759f5e4cb5501810affd17d3b11fee79b5ab628 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 09:20:56 +0200 Subject: [PATCH 01/25] set SDK --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 70db89729..02021d384 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "3.1.402" + "version": "5.0.100-rc.1.20452.10" } } \ No newline at end of file From f5913c1ddb90f3a7ca35ed15ac3c7146c3ea81f2 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 09:24:10 +0200 Subject: [PATCH 02/25] update CI to install SDK --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ac0c0f53..03075dff8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,8 @@ jobs: name: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} steps: + - name: 'Install .NET Core SDK' + uses: actions/setup-dotnet@v1 - uses: actions/checkout@af513c7a016048ae468971c52ed77d9562c7c819 - run: dotnet --info From 0dbf272e7b4c6342856c1d13a078dde4ba562cad Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 09:25:09 +0200 Subject: [PATCH 03/25] chaneg order in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03075dff8..055899703 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,9 +19,9 @@ jobs: name: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} steps: + - uses: actions/checkout@af513c7a016048ae468971c52ed77d9562c7c819 - name: 'Install .NET Core SDK' uses: actions/setup-dotnet@v1 - - uses: actions/checkout@af513c7a016048ae468971c52ed77d9562c7c819 - run: dotnet --info - if: contains(matrix.runs-on, 'macOS') || contains(matrix.runs-on, 'ubuntu') From 26e5d20abdf15f799cd2436f36a2136d77895849 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 09:29:10 +0200 Subject: [PATCH 04/25] install 2 SDKs with global.json --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 055899703..c0273a91e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,8 +20,12 @@ jobs: runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@af513c7a016048ae468971c52ed77d9562c7c819 - - name: 'Install .NET Core SDK' + - name: 'Install main .NET Core SDK' uses: actions/setup-dotnet@v1 + - name: 'Install 3.1 .NET Core SDK' + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '3.1.x' - run: dotnet --info - if: contains(matrix.runs-on, 'macOS') || contains(matrix.runs-on, 'ubuntu') From cc8a697143a48bb65221b912a971537b129695b5 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 09:31:14 +0200 Subject: [PATCH 05/25] install explicit versions --- .github/workflows/ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0273a91e..596053fb0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,12 +20,15 @@ jobs: runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@af513c7a016048ae468971c52ed77d9562c7c819 - - name: 'Install main .NET Core SDK' - uses: actions/setup-dotnet@v1 - - name: 'Install 3.1 .NET Core SDK' + - name: Setup dotnet + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '5.0.100-rc.1.20452.10' + - name: Setup dotnet uses: actions/setup-dotnet@v1 with: dotnet-version: '3.1.x' + - run: dotnet --info - if: contains(matrix.runs-on, 'macOS') || contains(matrix.runs-on, 'ubuntu') From 5ee85f6e603c91025cf3128ab90ea37075d7f090 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 09:34:40 +0200 Subject: [PATCH 06/25] fix yaml --- .github/workflows/ci.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 596053fb0..09c0aad8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,14 +20,15 @@ jobs: runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@af513c7a016048ae468971c52ed77d9562c7c819 + - name: Setup dotnet - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '5.0.100-rc.1.20452.10' + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '5.0.100-rc.1.20452.10' - name: Setup dotnet - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '3.1.x' + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '3.1.x' - run: dotnet --info From e6ab3d536aa8914fee3cc9bbed17a9a33070cd13 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 14:39:41 +0200 Subject: [PATCH 07/25] add condition for 3.1/ns2.0 --- src/Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index c56cb591c..0304cfacb 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,6 +1,6 @@ - + 3.1.0 3.1.0 3.1.0 From 3aaf8f67eecfa921c05dae0f5173aef8816bb1b0 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 14:54:16 +0200 Subject: [PATCH 08/25] add versions --- src/Directory.Build.targets | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 0304cfacb..2d4b78e9f 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,11 +1,21 @@ + + 5.0.0-* + + 3.1.0 3.1.0 3.1.0 - - 5.0.0-* + 5.6.0 + + + + 5.0.0-rc.1.20451.17 + 5.0.0-rc.1.20451.14 + 5.0.0-rc.1.20451.13 + 6.7.1 @@ -40,9 +50,8 @@ - - - + + From 8a89ee9d3812fbe756972dc7f11d25f75979da21 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 14:54:26 +0200 Subject: [PATCH 09/25] remove unused deps --- src/AspNetIdentity/host/Host.csproj | 1 - src/IdentityServer/host/Host.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/src/AspNetIdentity/host/Host.csproj b/src/AspNetIdentity/host/Host.csproj index e05fab5b8..20fff3c15 100644 --- a/src/AspNetIdentity/host/Host.csproj +++ b/src/AspNetIdentity/host/Host.csproj @@ -14,7 +14,6 @@ - diff --git a/src/IdentityServer/host/Host.csproj b/src/IdentityServer/host/Host.csproj index 4ed4eda49..dc04258d3 100644 --- a/src/IdentityServer/host/Host.csproj +++ b/src/IdentityServer/host/Host.csproj @@ -20,7 +20,6 @@ - From b40599301f988f1a6b0338d78a37da1a6aac6fd9 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 14:54:39 +0200 Subject: [PATCH 10/25] add net5 target to IS5 --- src/IdentityServer/src/Duende.IdentityServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IdentityServer/src/Duende.IdentityServer.csproj b/src/IdentityServer/src/Duende.IdentityServer.csproj index af0ecc05c..4d4a4029f 100644 --- a/src/IdentityServer/src/Duende.IdentityServer.csproj +++ b/src/IdentityServer/src/Duende.IdentityServer.csproj @@ -2,7 +2,7 @@ Duende.IdentityServer - netcoreapp3.1 + netcoreapp3.1;net5.0 OpenID Connect and OAuth 2.0 Framework for ASP.NET Core Duende.IdentityServer From b138cd327ecd66a0c6655a52b466a13023bdb36b Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 15:00:14 +0200 Subject: [PATCH 11/25] net5 target for aspid and ef --- .../src/Duende.IdentityServer.AspNetIdentity.csproj | 2 +- .../src/Duende.IdentityServer.EntityFramework.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AspNetIdentity/src/Duende.IdentityServer.AspNetIdentity.csproj b/src/AspNetIdentity/src/Duende.IdentityServer.AspNetIdentity.csproj index bff809739..7cb4bc281 100644 --- a/src/AspNetIdentity/src/Duende.IdentityServer.AspNetIdentity.csproj +++ b/src/AspNetIdentity/src/Duende.IdentityServer.AspNetIdentity.csproj @@ -2,7 +2,7 @@ Duende.IdentityServer.AspNetIdentity - netcoreapp3.1 + netcoreapp3.1;net5.0 ASP.NET Core Identity Integration for Duende IdentityServer Duende.IdentityServer.AspNetIdentity diff --git a/src/EntityFramework/src/Duende.IdentityServer.EntityFramework.csproj b/src/EntityFramework/src/Duende.IdentityServer.EntityFramework.csproj index 0f4829490..51c359ed5 100644 --- a/src/EntityFramework/src/Duende.IdentityServer.EntityFramework.csproj +++ b/src/EntityFramework/src/Duende.IdentityServer.EntityFramework.csproj @@ -2,7 +2,7 @@ Duende.IdentityServer.EntityFramework - netcoreapp3.1 + netcoreapp3.1;net5.0 EntityFramework persistence layer for Duende IdentityServer Duende.IdentityServer.EntityFramework From b572b22a7498d3d3d581ee4e4dc789ec72a5103c Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 15:03:57 +0200 Subject: [PATCH 12/25] add net5 target for storage --- .../src/Duende.IdentityServer.EntityFramework.Storage.csproj | 2 +- src/Storage/src/Duende.IdentityServer.Storage.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EntityFramework.Storage/src/Duende.IdentityServer.EntityFramework.Storage.csproj b/src/EntityFramework.Storage/src/Duende.IdentityServer.EntityFramework.Storage.csproj index ca94a0c16..1cd4abe65 100644 --- a/src/EntityFramework.Storage/src/Duende.IdentityServer.EntityFramework.Storage.csproj +++ b/src/EntityFramework.Storage/src/Duende.IdentityServer.EntityFramework.Storage.csproj @@ -1,7 +1,7 @@  Duende.IdentityServer.EntityFramework.Storage - netstandard2.0 + netstandard2.0;net5.0 EntityFramework persistence layer for Duende IdentityServer true diff --git a/src/Storage/src/Duende.IdentityServer.Storage.csproj b/src/Storage/src/Duende.IdentityServer.Storage.csproj index 826ddf516..9237fe775 100644 --- a/src/Storage/src/Duende.IdentityServer.Storage.csproj +++ b/src/Storage/src/Duende.IdentityServer.Storage.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.0;net5.0 Duende.IdentityServer.Storage Storage interfaces and models for Duende IdentityServer From 443d49210bc200e92e26a10ddd8e7c5c30567e9f Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sat, 10 Oct 2020 16:09:39 +0200 Subject: [PATCH 13/25] moar targets --- src/AspNetIdentity/host/Host.csproj | 5 +---- .../test/IntegrationTests/IntegrationTests.csproj | 2 +- src/EntityFramework.Storage/test/UnitTests/UnitTests.csproj | 2 +- src/EntityFramework/host/Host.csproj | 5 +---- src/IdentityServer/host/Host.csproj | 2 +- src/IdentityServer/test/UnitTests/UnitTests.csproj | 2 +- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/AspNetIdentity/host/Host.csproj b/src/AspNetIdentity/host/Host.csproj index 20fff3c15..2c0f98b38 100644 --- a/src/AspNetIdentity/host/Host.csproj +++ b/src/AspNetIdentity/host/Host.csproj @@ -1,14 +1,11 @@  - netcoreapp3.1 + netcoreapp3.1;net5.0 - - - diff --git a/src/EntityFramework.Storage/test/IntegrationTests/IntegrationTests.csproj b/src/EntityFramework.Storage/test/IntegrationTests/IntegrationTests.csproj index e0bf3ab76..e87f24a59 100644 --- a/src/EntityFramework.Storage/test/IntegrationTests/IntegrationTests.csproj +++ b/src/EntityFramework.Storage/test/IntegrationTests/IntegrationTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.1;net5.0 diff --git a/src/EntityFramework.Storage/test/UnitTests/UnitTests.csproj b/src/EntityFramework.Storage/test/UnitTests/UnitTests.csproj index 2282622fd..0ec514e29 100644 --- a/src/EntityFramework.Storage/test/UnitTests/UnitTests.csproj +++ b/src/EntityFramework.Storage/test/UnitTests/UnitTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.1;net5.0 diff --git a/src/EntityFramework/host/Host.csproj b/src/EntityFramework/host/Host.csproj index f0436ed01..01958e375 100644 --- a/src/EntityFramework/host/Host.csproj +++ b/src/EntityFramework/host/Host.csproj @@ -1,13 +1,10 @@  - netcoreapp3.1 + netcoreapp3.1;net5.0 - - - diff --git a/src/IdentityServer/host/Host.csproj b/src/IdentityServer/host/Host.csproj index dc04258d3..95e5a62db 100644 --- a/src/IdentityServer/host/Host.csproj +++ b/src/IdentityServer/host/Host.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.1;net5.0 InProcess diff --git a/src/IdentityServer/test/UnitTests/UnitTests.csproj b/src/IdentityServer/test/UnitTests/UnitTests.csproj index 6aaba9a89..e080049fa 100644 --- a/src/IdentityServer/test/UnitTests/UnitTests.csproj +++ b/src/IdentityServer/test/UnitTests/UnitTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.1;net5.0 From e99a30f7844026464e5a0fca997746027cd4fb1a Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Mon, 12 Oct 2020 12:34:48 +0200 Subject: [PATCH 14/25] enable integration tests - skip two tests that need investigatiokn --- .../test/IntegrationTests/Clients/ClientCredentialsClient.cs | 2 +- .../Endpoints/Authorize/JwtRequestAuthorizeTests.cs | 2 +- .../test/IntegrationTests/IntegrationTests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/IdentityServer/test/IntegrationTests/Clients/ClientCredentialsClient.cs b/src/IdentityServer/test/IntegrationTests/Clients/ClientCredentialsClient.cs index 464fdb9a2..8a93003d0 100644 --- a/src/IdentityServer/test/IntegrationTests/Clients/ClientCredentialsClient.cs +++ b/src/IdentityServer/test/IntegrationTests/Clients/ClientCredentialsClient.cs @@ -117,7 +117,7 @@ public async Task Valid_request_multiple_audiences_should_return_expected_payloa scopes.First().ToString().Should().Be("api1"); } - [Fact] + [Fact(Skip = "Revisit for .NET 5")] public async Task Valid_request_with_confirmation_should_return_expected_payload() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest diff --git a/src/IdentityServer/test/IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs b/src/IdentityServer/test/IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs index 6e08c9abe..1fedc22ee 100644 --- a/src/IdentityServer/test/IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs +++ b/src/IdentityServer/test/IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs @@ -484,7 +484,7 @@ public async Task mismatch_in_jwt_values_should_error() _mockPipeline.LoginRequest.Should().BeNull(); } - [Fact] + [Fact(Skip = "Revisit for .NET 5")] [Trait("Category", Category)] public async Task authorize_should_accept_complex_objects_in_request_object() { diff --git a/src/IdentityServer/test/IntegrationTests/IntegrationTests.csproj b/src/IdentityServer/test/IntegrationTests/IntegrationTests.csproj index ec75caf41..9dc5ecf70 100644 --- a/src/IdentityServer/test/IntegrationTests/IntegrationTests.csproj +++ b/src/IdentityServer/test/IntegrationTests/IntegrationTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + netcoreapp3.1;net5.0 From 4b2c085659d86bcf6fb1cee1084dae1163efb0ec Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Wed, 14 Oct 2020 11:58:48 +0200 Subject: [PATCH 15/25] add updated JWT creation logic for NET5 --- .../src/Extensions/TokenExtensions.cs | 152 +++++++++++++++++- .../Default/DefaultTokenCreationService.cs | 35 ++++ 2 files changed, 180 insertions(+), 7 deletions(-) diff --git a/src/IdentityServer/src/Extensions/TokenExtensions.cs b/src/IdentityServer/src/Extensions/TokenExtensions.cs index 2917cb17a..5a81fe9d9 100644 --- a/src/IdentityServer/src/Extensions/TokenExtensions.cs +++ b/src/IdentityServer/src/Extensions/TokenExtensions.cs @@ -12,7 +12,9 @@ using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; +using System.Text.Json; using Duende.IdentityServer.Configuration; +using Microsoft.AspNetCore.Routing; namespace Duende.IdentityServer.Extensions { @@ -31,7 +33,8 @@ public static class TokenExtensions /// /// /// - public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, IdentityServerOptions options, ILogger logger) + public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, IdentityServerOptions options, + ILogger logger) { var payload = new JwtPayload( token.Issuer, @@ -47,12 +50,14 @@ public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, var amrClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.AuthenticationMethod).ToArray(); var scopeClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.Scope).ToArray(); - var jsonClaims = token.Claims.Where(x => x.ValueType == IdentityServerConstants.ClaimValueTypes.Json).ToList(); - + var jsonClaims = token.Claims.Where(x => x.ValueType == IdentityServerConstants.ClaimValueTypes.Json) + .ToList(); + // add confirmation claim if present (it's JSON valued) if (token.Confirmation.IsPresent()) { - jsonClaims.Add(new Claim(JwtClaimTypes.Confirmation, token.Confirmation, IdentityServerConstants.ClaimValueTypes.Json)); + jsonClaims.Add(new Claim(JwtClaimTypes.Confirmation, token.Confirmation, + IdentityServerConstants.ClaimValueTypes.Json)); } var normalClaims = token.Claims @@ -83,7 +88,7 @@ public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, var amrValues = amrClaims.Select(x => x.Value).Distinct().ToArray(); payload.Add(JwtClaimTypes.AuthenticationMethod, amrValues); } - + // deal with json types // calling ToArray() to trigger JSON parsing once and so later // collection identity comparisons work for the anonymous type @@ -97,7 +102,8 @@ public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, { if (payload.ContainsKey(group.Key)) { - throw new Exception($"Can't add two claims where one is a JSON object and the other is not a JSON object ({group.Key})"); + throw new Exception( + $"Can't add two claims where one is a JSON object and the other is not a JSON object ({group.Key})"); } if (group.Skip(1).Any()) @@ -125,7 +131,7 @@ public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, var newArr = new List(); foreach (var arrays in group) { - var arr = (JArray)arrays.JsonValue; + var arr = (JArray) arrays.JsonValue; newArr.AddRange(arr); } @@ -149,5 +155,137 @@ public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, throw; } } + + /// + /// Creates the default JWT payload dictionary + /// + /// + /// + /// + /// + /// + public static Dictionary CreateJwtPayloadDictionary(this Token token, + IdentityServerOptions options, ISystemClock clock, ILogger logger) + { + try + { + var payload = new Dictionary(); + + // set issuer + payload.Add(JwtClaimTypes.Issuer, token.Issuer); + + // set times (nbf, exp) - what about iat? + var now = clock.UtcNow.ToUnixTimeSeconds(); + var exp = now + token.Lifetime; + + //payload.Add(JwtClaimTypes.IssuedAt, now); + payload.Add(JwtClaimTypes.NotBefore, now); + payload.Add(JwtClaimTypes.Expiration, exp); + + // add audience claim(s) + if (token.Audiences.Any()) + { + if (token.Audiences.Count == 1) + { + payload.Add(JwtClaimTypes.Audience, token.Audiences.First()); + } + else + { + payload.Add(JwtClaimTypes.Audience, token.Audiences); + } + } + + // add confirmation claim (if present) + if (token.Confirmation.IsPresent()) + { + payload.Add(JwtClaimTypes.Confirmation, + JsonSerializer.Deserialize(token.Confirmation)); + } + + // scope claims + var scopeClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.Scope).ToArray(); + if (!scopeClaims.IsNullOrEmpty()) + { + var scopeValues = scopeClaims.Select(x => x.Value).ToArray(); + + if (options.EmitScopesAsSpaceDelimitedStringInJwt) + { + payload.Add(JwtClaimTypes.Scope, string.Join(" ", scopeValues)); + } + else + { + payload.Add(JwtClaimTypes.Scope, scopeValues); + } + } + + // amr claims + var amrClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.AuthenticationMethod).ToArray(); + if (!amrClaims.IsNullOrEmpty()) + { + var amrValues = amrClaims.Select(x => x.Value).Distinct().ToArray(); + payload.Add(JwtClaimTypes.AuthenticationMethod, amrValues); + } + + var simpleClaimTypes = token.Claims.Where(c => + c.Type != JwtClaimTypes.AuthenticationMethod && c.Type != JwtClaimTypes.Scope) + .Select(c => c.Type) + .Distinct(); + + // other claims + foreach (var claimType in simpleClaimTypes) + { + var claims = token.Claims.Where(c => c.Type == claimType).ToArray(); + + if (claims.Count() > 1) + { + payload.Add(claimType, AddObjects(claims)); + } + else + { + payload.Add(claimType, AddObject(claims.First())); + } + } + + return payload; + } + catch (Exception ex) + { + logger.LogCritical(ex, "Error creating the JWT payload"); + throw; + } + } + + private static IEnumerable AddObjects(IEnumerable claims) + { + foreach (var claim in claims) + { + yield return AddObject(claim); + } + } + + private static object AddObject(Claim claim) + { + if (claim.Type == ClaimValueTypes.Boolean) + { + return Boolean.Parse(claim.Value); + } + + if (claim.Type == ClaimValueTypes.Integer || claim.Type == ClaimValueTypes.Integer32) + { + return Int32.Parse(claim.Value); + } + + if (claim.Type == ClaimValueTypes.Integer64) + { + return Int64.Parse(claim.Value); + } + + if (claim.Type == IdentityServerConstants.ClaimValueTypes.Json) + { + return JsonSerializer.Deserialize(claim.Value); + } + + return claim.Value; + } } } \ No newline at end of file diff --git a/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs b/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs index 7dd276f20..b590bce28 100644 --- a/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs +++ b/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs @@ -9,11 +9,15 @@ using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using System; +using System.Collections.Generic; using System.Globalization; using System.IdentityModel.Tokens.Jwt; +using System.Text.Json; using System.Threading.Tasks; using Duende.IdentityServer.Configuration; +using Microsoft.IdentityModel.JsonWebTokens; + namespace Duende.IdentityServer.Services { /// @@ -69,10 +73,41 @@ public DefaultTokenCreationService( /// public virtual async Task CreateTokenAsync(Token token) { +#if NET5_0 + return await CreateJsonWebTokenAsync(token); +#elif NETCOREAPP3_1 var header = await CreateHeaderAsync(token); var payload = await CreatePayloadAsync(token); return await CreateJwtAsync(new JwtSecurityToken(header, payload)); +#endif + + } + + private async Task CreateJsonWebTokenAsync(Token token) + { + var payload = token.CreateJwtPayloadDictionary(Options, Clock, Logger); + var json = JsonSerializer.Serialize(payload); + + var credential = await Keys.GetSigningCredentialsAsync(token.AllowedSigningAlgorithms); + + if (credential == null) + { + throw new InvalidOperationException("No signing credential is configured. Can't create JWT token"); + } + + var additionalHeaderElements = new Dictionary(); + + if (token.Type == IdentityServerConstants.TokenTypes.AccessToken) + { + if (Options.AccessTokenJwtType.IsPresent()) + { + additionalHeaderElements.Add("typ", Options.AccessTokenJwtType); + } + } + + var handler = new JsonWebTokenHandler { SetDefaultTimesOnTokenCreation = false }; + return handler.CreateToken(json, credential, additionalHeaderElements); } /// From 1aa814d2741a7abae06dd3e2fac02d7f171ad0d6 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Wed, 14 Oct 2020 11:59:03 +0200 Subject: [PATCH 16/25] update tests --- .../test/IntegrationTests/Clients/ClientCredentialsClient.cs | 2 +- .../DeviceAuthorizationResponseGeneratorTests.cs | 2 +- .../ResponseHandling/UserInfoResponseGeneratorTests.cs | 2 +- .../test/UnitTests/Stores/InMemoryClientStoreTests.cs | 2 +- .../test/UnitTests/Stores/InMemoryDeviceFlowStoreTests.cs | 2 +- .../test/UnitTests/Stores/InMemoryPersistedGrantStoreTests.cs | 2 +- .../test/UnitTests/Stores/InMemoryResourcesStoreTests.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/IdentityServer/test/IntegrationTests/Clients/ClientCredentialsClient.cs b/src/IdentityServer/test/IntegrationTests/Clients/ClientCredentialsClient.cs index 8a93003d0..464fdb9a2 100644 --- a/src/IdentityServer/test/IntegrationTests/Clients/ClientCredentialsClient.cs +++ b/src/IdentityServer/test/IntegrationTests/Clients/ClientCredentialsClient.cs @@ -117,7 +117,7 @@ public async Task Valid_request_multiple_audiences_should_return_expected_payloa scopes.First().ToString().Should().Be("api1"); } - [Fact(Skip = "Revisit for .NET 5")] + [Fact] public async Task Valid_request_with_confirmation_should_return_expected_payload() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest diff --git a/src/IdentityServer/test/UnitTests/ResponseHandling/DeviceAuthorizationResponseGeneratorTests.cs b/src/IdentityServer/test/UnitTests/ResponseHandling/DeviceAuthorizationResponseGeneratorTests.cs index b2ec6bdf5..c57b48461 100644 --- a/src/IdentityServer/test/UnitTests/ResponseHandling/DeviceAuthorizationResponseGeneratorTests.cs +++ b/src/IdentityServer/test/UnitTests/ResponseHandling/DeviceAuthorizationResponseGeneratorTests.cs @@ -18,7 +18,7 @@ using Microsoft.Extensions.Logging.Abstractions; using Xunit; -namespace IdentityServer.UnitTests.ResponseHandling +namespace UnitTests.ResponseHandling { public class DeviceAuthorizationResponseGeneratorTests { diff --git a/src/IdentityServer/test/UnitTests/ResponseHandling/UserInfoResponseGeneratorTests.cs b/src/IdentityServer/test/UnitTests/ResponseHandling/UserInfoResponseGeneratorTests.cs index 37b79c68d..199c1a130 100644 --- a/src/IdentityServer/test/UnitTests/ResponseHandling/UserInfoResponseGeneratorTests.cs +++ b/src/IdentityServer/test/UnitTests/ResponseHandling/UserInfoResponseGeneratorTests.cs @@ -16,7 +16,7 @@ using UnitTests.Common; using Xunit; -namespace IdentityServer.UnitTests.ResponseHandling +namespace UnitTests.ResponseHandling { public class UserInfoResponseGeneratorTests { diff --git a/src/IdentityServer/test/UnitTests/Stores/InMemoryClientStoreTests.cs b/src/IdentityServer/test/UnitTests/Stores/InMemoryClientStoreTests.cs index a7a7b73e1..1dd444871 100644 --- a/src/IdentityServer/test/UnitTests/Stores/InMemoryClientStoreTests.cs +++ b/src/IdentityServer/test/UnitTests/Stores/InMemoryClientStoreTests.cs @@ -9,7 +9,7 @@ using Xunit; using FluentAssertions; -namespace IdentityServer.UnitTests.Stores +namespace UnitTests.Stores { public class InMemoryClientStoreTests { diff --git a/src/IdentityServer/test/UnitTests/Stores/InMemoryDeviceFlowStoreTests.cs b/src/IdentityServer/test/UnitTests/Stores/InMemoryDeviceFlowStoreTests.cs index f385312a2..e96991372 100644 --- a/src/IdentityServer/test/UnitTests/Stores/InMemoryDeviceFlowStoreTests.cs +++ b/src/IdentityServer/test/UnitTests/Stores/InMemoryDeviceFlowStoreTests.cs @@ -11,7 +11,7 @@ using FluentAssertions; using Xunit; -namespace IdentityServer.UnitTests.Stores +namespace UnitTests.Stores { public class InMemoryDeviceFlowStoreTests { diff --git a/src/IdentityServer/test/UnitTests/Stores/InMemoryPersistedGrantStoreTests.cs b/src/IdentityServer/test/UnitTests/Stores/InMemoryPersistedGrantStoreTests.cs index 2f8f8135c..50cb94142 100644 --- a/src/IdentityServer/test/UnitTests/Stores/InMemoryPersistedGrantStoreTests.cs +++ b/src/IdentityServer/test/UnitTests/Stores/InMemoryPersistedGrantStoreTests.cs @@ -9,7 +9,7 @@ using Duende.IdentityServer.Stores; using FluentAssertions; -namespace IdentityServer.UnitTests.Stores +namespace UnitTests.Stores { public class InMemoryPersistedGrantStoreTests { diff --git a/src/IdentityServer/test/UnitTests/Stores/InMemoryResourcesStoreTests.cs b/src/IdentityServer/test/UnitTests/Stores/InMemoryResourcesStoreTests.cs index ab194be47..fd87b7add 100644 --- a/src/IdentityServer/test/UnitTests/Stores/InMemoryResourcesStoreTests.cs +++ b/src/IdentityServer/test/UnitTests/Stores/InMemoryResourcesStoreTests.cs @@ -9,7 +9,7 @@ using Xunit; using FluentAssertions; -namespace IdentityServer.UnitTests.Stores +namespace UnitTests.Stores { public class InMemoryResourcesStoreTests { From c08a7bba2009c0db1d4c484800c1a445bc0a6d7e Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Wed, 14 Oct 2020 12:02:59 +0200 Subject: [PATCH 17/25] update to RC2 --- .github/workflows/ci.yml | 2 +- global.json | 2 +- src/Directory.Build.targets | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09c0aad8a..d161bb39c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '5.0.100-rc.1.20452.10' + dotnet-version: '5.0.100-rc.2.20479.15' - name: Setup dotnet uses: actions/setup-dotnet@v1 with: diff --git a/global.json b/global.json index 02021d384..2a7b742c5 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "5.0.100-rc.1.20452.10" + "version": "5.0.100-rc.2.20479.15" } } \ No newline at end of file diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 2d4b78e9f..5aeb16da6 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -12,10 +12,10 @@ - 5.0.0-rc.1.20451.17 - 5.0.0-rc.1.20451.14 - 5.0.0-rc.1.20451.13 - 6.7.1 + 5.0.0-rc.2.* + 5.0.0-rc.2.* + 5.0.0-rc.2.* + 6.8.0 From 8f2d4c053ae3cdae0a0f6e7a1898b8d8394da0d4 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Wed, 14 Oct 2020 14:30:01 +0200 Subject: [PATCH 18/25] update RO payload logic --- .../Validation/Default/JwtRequestValidator.cs | 27 ++++++++++--------- .../Authorize/JwtRequestAuthorizeTests.cs | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/IdentityServer/src/Validation/Default/JwtRequestValidator.cs b/src/IdentityServer/src/Validation/Default/JwtRequestValidator.cs index 1fafdc6cf..584cdaafd 100644 --- a/src/IdentityServer/src/Validation/Default/JwtRequestValidator.cs +++ b/src/IdentityServer/src/Validation/Default/JwtRequestValidator.cs @@ -199,19 +199,20 @@ protected virtual Task> ProcessPayloadAsync(JwtSecuri if (!Constants.Filters.JwtRequestClaimTypesFilter.Contains(key)) { var value = token.Payload[key]; - - switch (value) - { - case string s: - payload.Add(key, s); - break; - case JObject jobj: - payload.Add(key, jobj.ToString(Formatting.None)); - break; - case JArray jarr: - payload.Add(key, jarr.ToString(Formatting.None)); - break; - } + payload.Add(key, value.ToString()); + + // switch (value) + // { + // case string s: + // payload.Add(key, s); + // break; + // case JObject jobj: + // payload.Add(key, jobj.ToString(Formatting.None)); + // break; + // case JArray jarr: + // payload.Add(key, jarr.ToString(Formatting.None)); + // break; + // } } } diff --git a/src/IdentityServer/test/IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs b/src/IdentityServer/test/IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs index 1fedc22ee..6e08c9abe 100644 --- a/src/IdentityServer/test/IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs +++ b/src/IdentityServer/test/IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs @@ -484,7 +484,7 @@ public async Task mismatch_in_jwt_values_should_error() _mockPipeline.LoginRequest.Should().BeNull(); } - [Fact(Skip = "Revisit for .NET 5")] + [Fact] [Trait("Category", Category)] public async Task authorize_should_accept_complex_objects_in_request_object() { From 69e44efe9549d0eb958429aa17cb850792688f53 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Fri, 16 Oct 2020 10:38:21 +0200 Subject: [PATCH 19/25] unify JWT creation --- .../Default/DefaultTokenCreationService.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs b/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs index b590bce28..9e5e86faa 100644 --- a/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs +++ b/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs @@ -73,14 +73,14 @@ public DefaultTokenCreationService( /// public virtual async Task CreateTokenAsync(Token token) { -#if NET5_0 +// #if NET5_0 return await CreateJsonWebTokenAsync(token); -#elif NETCOREAPP3_1 - var header = await CreateHeaderAsync(token); - var payload = await CreatePayloadAsync(token); - - return await CreateJwtAsync(new JwtSecurityToken(header, payload)); -#endif +// #elif NETCOREAPP3_1 +// var header = await CreateHeaderAsync(token); +// var payload = await CreatePayloadAsync(token); +// +// return await CreateJwtAsync(new JwtSecurityToken(header, payload)); +// #endif } From 771d2d5738869765a6310fdf2ff1d44d125cffd6 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Fri, 16 Oct 2020 10:58:57 +0200 Subject: [PATCH 20/25] centralize iat handling --- src/IdentityServer/src/Extensions/TokenExtensions.cs | 6 +++--- .../Services/Default/DefaultBackChannelLogoutService.cs | 1 - .../src/Services/Default/DefaultTokenService.cs | 7 ++----- .../test/UnitTests/Validation/AccessTokenValidation.cs | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/IdentityServer/src/Extensions/TokenExtensions.cs b/src/IdentityServer/src/Extensions/TokenExtensions.cs index 5a81fe9d9..5b94de45b 100644 --- a/src/IdentityServer/src/Extensions/TokenExtensions.cs +++ b/src/IdentityServer/src/Extensions/TokenExtensions.cs @@ -174,12 +174,12 @@ public static Dictionary CreateJwtPayloadDictionary(this Token t // set issuer payload.Add(JwtClaimTypes.Issuer, token.Issuer); - // set times (nbf, exp) - what about iat? + // set times (nbf, exp, iat) var now = clock.UtcNow.ToUnixTimeSeconds(); var exp = now + token.Lifetime; - - //payload.Add(JwtClaimTypes.IssuedAt, now); + payload.Add(JwtClaimTypes.NotBefore, now); + payload.Add(JwtClaimTypes.IssuedAt, now); payload.Add(JwtClaimTypes.Expiration, exp); // add audience claim(s) diff --git a/src/IdentityServer/src/Services/Default/DefaultBackChannelLogoutService.cs b/src/IdentityServer/src/Services/Default/DefaultBackChannelLogoutService.cs index ca18add50..f009fa633 100644 --- a/src/IdentityServer/src/Services/Default/DefaultBackChannelLogoutService.cs +++ b/src/IdentityServer/src/Services/Default/DefaultBackChannelLogoutService.cs @@ -164,7 +164,6 @@ protected Task> CreateClaimsForTokenAsync(BackChannelLogoutRe { new Claim(JwtClaimTypes.Subject, request.SubjectId), new Claim(JwtClaimTypes.Audience, request.ClientId), - new Claim(JwtClaimTypes.IssuedAt, Clock.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64), new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId(16, CryptoRandom.OutputFormat.Hex)), new Claim(JwtClaimTypes.Events, json, IdentityServerConstants.ClaimValueTypes.Json) }; diff --git a/src/IdentityServer/src/Services/Default/DefaultTokenService.cs b/src/IdentityServer/src/Services/Default/DefaultTokenService.cs index 70d95edef..52bec9cdc 100644 --- a/src/IdentityServer/src/Services/Default/DefaultTokenService.cs +++ b/src/IdentityServer/src/Services/Default/DefaultTokenService.cs @@ -124,8 +124,9 @@ public virtual async Task CreateIdentityTokenAsync(TokenCreationRequest r claims.Add(new Claim(JwtClaimTypes.Nonce, request.Nonce)); } + // todo: cleanup // add iat claim - claims.Add(new Claim(JwtClaimTypes.IssuedAt, Clock.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)); + //claims.Add(new Claim(JwtClaimTypes.IssuedAt, Clock.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)); // add at_hash claim if (request.AccessTokenToHash.IsPresent()) @@ -202,10 +203,6 @@ public virtual async Task CreateAccessTokenAsync(TokenCreationRequest req claims.Add(new Claim(JwtClaimTypes.SessionId, request.ValidatedRequest.SessionId)); } - // iat claim as required by JWT profile - claims.Add(new Claim(JwtClaimTypes.IssuedAt, Clock.UtcNow.ToUnixTimeSeconds().ToString(), - ClaimValueTypes.Integer64)); - var issuer = ContextAccessor.HttpContext.GetIdentityServerIssuerUri(); var token = new Token(OidcConstants.TokenTypes.AccessToken) { diff --git a/src/IdentityServer/test/UnitTests/Validation/AccessTokenValidation.cs b/src/IdentityServer/test/UnitTests/Validation/AccessTokenValidation.cs index b0ecb2fcc..bba897f0c 100644 --- a/src/IdentityServer/test/UnitTests/Validation/AccessTokenValidation.cs +++ b/src/IdentityServer/test/UnitTests/Validation/AccessTokenValidation.cs @@ -188,7 +188,7 @@ public async Task JWT_Token_with_scopes_have_expected_claims(bool flag) result.Jwt.Should().NotBeNullOrEmpty(); result.Client.ClientId.Should().Be("roclient"); - result.Claims.Count().Should().Be(8); + result.Claims.Count().Should().Be(9); var scopes = result.Claims.Where(c => c.Type == "scope").Select(c => c.Value).ToArray(); scopes.Count().Should().Be(2); scopes[0].Should().Be("read"); From 32d162d40c80e8f11fd006a04bbc2270eb4aaf2f Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sun, 18 Oct 2020 12:09:46 +0200 Subject: [PATCH 21/25] unify JWT creation --- .../Default/DefaultTokenCreationService.cs | 97 ++++++------------- 1 file changed, 29 insertions(+), 68 deletions(-) diff --git a/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs b/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs index 9e5e86faa..ac25bd625 100644 --- a/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs +++ b/src/IdentityServer/src/Services/Default/DefaultTokenCreationService.cs @@ -73,29 +73,30 @@ public DefaultTokenCreationService( /// public virtual async Task CreateTokenAsync(Token token) { -// #if NET5_0 - return await CreateJsonWebTokenAsync(token); -// #elif NETCOREAPP3_1 -// var header = await CreateHeaderAsync(token); -// var payload = await CreatePayloadAsync(token); -// -// return await CreateJwtAsync(new JwtSecurityToken(header, payload)); -// #endif + var payload = await CreatePayloadAsync(token); + var headerElements = await CreateHeaderElementsAsync(token); + return await CreateJwtAsync(token, payload, headerElements); } - private async Task CreateJsonWebTokenAsync(Token token) + /// + /// Creates the JWT payload + /// + /// + /// + protected virtual Task CreatePayloadAsync(Token token) { var payload = token.CreateJwtPayloadDictionary(Options, Clock, Logger); - var json = JsonSerializer.Serialize(payload); - - var credential = await Keys.GetSigningCredentialsAsync(token.AllowedSigningAlgorithms); - - if (credential == null) - { - throw new InvalidOperationException("No signing credential is configured. Can't create JWT token"); - } + return Task.FromResult(JsonSerializer.Serialize(payload)); + } + /// + /// Creates additional JWT header elements + /// + /// + /// + protected virtual Task> CreateHeaderElementsAsync(Token token) + { var additionalHeaderElements = new Dictionary(); if (token.Type == IdentityServerConstants.TokenTypes.AccessToken) @@ -106,16 +107,19 @@ private async Task CreateJsonWebTokenAsync(Token token) } } - var handler = new JsonWebTokenHandler { SetDefaultTimesOnTokenCreation = false }; - return handler.CreateToken(json, credential, additionalHeaderElements); + return Task.FromResult(additionalHeaderElements); } /// - /// Creates the JWT header + /// Creates JWT token /// - /// The token. - /// The JWT header - protected virtual async Task CreateHeaderAsync(Token token) + /// + /// + /// + /// + /// + protected virtual async Task CreateJwtAsync(Token token, string payload, + Dictionary headerElements) { var credential = await Keys.GetSigningCredentialsAsync(token.AllowedSigningAlgorithms); @@ -124,51 +128,8 @@ protected virtual async Task CreateHeaderAsync(Token token) throw new InvalidOperationException("No signing credential is configured. Can't create JWT token"); } - var header = new JwtHeader(credential); - - // emit x5t claim for backwards compatibility with v4 of MS JWT library - if (credential.Key is X509SecurityKey x509Key) - { - var cert = x509Key.Certificate; - if (Clock.UtcNow.UtcDateTime > cert.NotAfter) - { - Logger.LogWarning("Certificate {subjectName} has expired on {expiration}", cert.Subject, cert.NotAfter.ToString(CultureInfo.InvariantCulture)); - } - - header["x5t"] = Base64Url.Encode(cert.GetCertHash()); - } - - if (token.Type == IdentityServerConstants.TokenTypes.AccessToken) - { - if (Options.AccessTokenJwtType.IsPresent()) - { - header["typ"] = Options.AccessTokenJwtType; - } - } - - return header; - } - - /// - /// Creates the JWT payload - /// - /// The token. - /// The JWT payload - protected virtual Task CreatePayloadAsync(Token token) - { - var payload = token.CreateJwtPayload(Clock, Options, Logger); - return Task.FromResult(payload); - } - - /// - /// Applies the signature to the JWT - /// - /// The JWT object. - /// The signed JWT - protected virtual Task CreateJwtAsync(JwtSecurityToken jwt) - { - var handler = new JwtSecurityTokenHandler(); - return Task.FromResult(handler.WriteToken(jwt)); + var handler = new JsonWebTokenHandler { SetDefaultTimesOnTokenCreation = false }; + return handler.CreateToken(payload, credential, headerElements); } } } \ No newline at end of file From 41b6cc044f0d770258f3623463d47611ab9f32e4 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sun, 18 Oct 2020 15:38:49 +0200 Subject: [PATCH 22/25] cleanup old code --- .../src/Extensions/TokenExtensions.cs | 134 ------------------ .../Validation/Default/JwtRequestValidator.cs | 15 -- .../Extensions/JwtPayloadCreationTests.cs | 73 ---------- 3 files changed, 222 deletions(-) delete mode 100644 src/IdentityServer/test/UnitTests/Extensions/JwtPayloadCreationTests.cs diff --git a/src/IdentityServer/src/Extensions/TokenExtensions.cs b/src/IdentityServer/src/Extensions/TokenExtensions.cs index 5b94de45b..ec4a1b474 100644 --- a/src/IdentityServer/src/Extensions/TokenExtensions.cs +++ b/src/IdentityServer/src/Extensions/TokenExtensions.cs @@ -6,7 +6,6 @@ using Duende.IdentityServer.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; @@ -23,139 +22,6 @@ namespace Duende.IdentityServer.Extensions /// public static class TokenExtensions { - /// - /// Creates the default JWT payload. - /// - /// The token. - /// The clock. - /// The options - /// The logger. - /// - /// - /// - public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, IdentityServerOptions options, - ILogger logger) - { - var payload = new JwtPayload( - token.Issuer, - null, - null, - clock.UtcNow.UtcDateTime, - clock.UtcNow.UtcDateTime.AddSeconds(token.Lifetime)); - - foreach (var aud in token.Audiences) - { - payload.AddClaim(new Claim(JwtClaimTypes.Audience, aud)); - } - - var amrClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.AuthenticationMethod).ToArray(); - var scopeClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.Scope).ToArray(); - var jsonClaims = token.Claims.Where(x => x.ValueType == IdentityServerConstants.ClaimValueTypes.Json) - .ToList(); - - // add confirmation claim if present (it's JSON valued) - if (token.Confirmation.IsPresent()) - { - jsonClaims.Add(new Claim(JwtClaimTypes.Confirmation, token.Confirmation, - IdentityServerConstants.ClaimValueTypes.Json)); - } - - var normalClaims = token.Claims - .Except(amrClaims) - .Except(jsonClaims) - .Except(scopeClaims); - - payload.AddClaims(normalClaims); - - // scope claims - if (!scopeClaims.IsNullOrEmpty()) - { - var scopeValues = scopeClaims.Select(x => x.Value).ToArray(); - - if (options.EmitScopesAsSpaceDelimitedStringInJwt) - { - payload.Add(JwtClaimTypes.Scope, string.Join(" ", scopeValues)); - } - else - { - payload.Add(JwtClaimTypes.Scope, scopeValues); - } - } - - // amr claims - if (!amrClaims.IsNullOrEmpty()) - { - var amrValues = amrClaims.Select(x => x.Value).Distinct().ToArray(); - payload.Add(JwtClaimTypes.AuthenticationMethod, amrValues); - } - - // deal with json types - // calling ToArray() to trigger JSON parsing once and so later - // collection identity comparisons work for the anonymous type - try - { - var jsonTokens = jsonClaims.Select(x => new { x.Type, JsonValue = JRaw.Parse(x.Value) }).ToArray(); - - var jsonObjects = jsonTokens.Where(x => x.JsonValue.Type == JTokenType.Object).ToArray(); - var jsonObjectGroups = jsonObjects.GroupBy(x => x.Type).ToArray(); - foreach (var group in jsonObjectGroups) - { - if (payload.ContainsKey(group.Key)) - { - throw new Exception( - $"Can't add two claims where one is a JSON object and the other is not a JSON object ({group.Key})"); - } - - if (group.Skip(1).Any()) - { - // add as array - payload.Add(group.Key, group.Select(x => x.JsonValue).ToArray()); - } - else - { - // add just one - payload.Add(group.Key, group.First().JsonValue); - } - } - - var jsonArrays = jsonTokens.Where(x => x.JsonValue.Type == JTokenType.Array).ToArray(); - var jsonArrayGroups = jsonArrays.GroupBy(x => x.Type).ToArray(); - foreach (var group in jsonArrayGroups) - { - if (payload.ContainsKey(group.Key)) - { - throw new Exception( - $"Can't add two claims where one is a JSON array and the other is not a JSON array ({group.Key})"); - } - - var newArr = new List(); - foreach (var arrays in group) - { - var arr = (JArray) arrays.JsonValue; - newArr.AddRange(arr); - } - - // add just one array for the group/key/claim type - payload.Add(group.Key, newArr.ToArray()); - } - - var unsupportedJsonTokens = jsonTokens.Except(jsonObjects).Except(jsonArrays).ToArray(); - var unsupportedJsonClaimTypes = unsupportedJsonTokens.Select(x => x.Type).Distinct().ToArray(); - if (unsupportedJsonClaimTypes.Any()) - { - throw new Exception( - $"Unsupported JSON type for claim types: {unsupportedJsonClaimTypes.Aggregate((x, y) => x + ", " + y)}"); - } - - return payload; - } - catch (Exception ex) - { - logger.LogCritical(ex, "Error creating a JSON valued claim"); - throw; - } - } - /// /// Creates the default JWT payload dictionary /// diff --git a/src/IdentityServer/src/Validation/Default/JwtRequestValidator.cs b/src/IdentityServer/src/Validation/Default/JwtRequestValidator.cs index 584cdaafd..5490b25c2 100644 --- a/src/IdentityServer/src/Validation/Default/JwtRequestValidator.cs +++ b/src/IdentityServer/src/Validation/Default/JwtRequestValidator.cs @@ -14,8 +14,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Duende.IdentityServer.Validation { @@ -200,19 +198,6 @@ protected virtual Task> ProcessPayloadAsync(JwtSecuri { var value = token.Payload[key]; payload.Add(key, value.ToString()); - - // switch (value) - // { - // case string s: - // payload.Add(key, s); - // break; - // case JObject jobj: - // payload.Add(key, jobj.ToString(Formatting.None)); - // break; - // case JArray jarr: - // payload.Add(key, jarr.ToString(Formatting.None)); - // break; - // } } } diff --git a/src/IdentityServer/test/UnitTests/Extensions/JwtPayloadCreationTests.cs b/src/IdentityServer/test/UnitTests/Extensions/JwtPayloadCreationTests.cs deleted file mode 100644 index 7606f7020..000000000 --- a/src/IdentityServer/test/UnitTests/Extensions/JwtPayloadCreationTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Duende Software. All rights reserved. -// See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using Duende.IdentityServer.Configuration; -using Duende.IdentityServer.Extensions; -using Duende.IdentityServer.Models; -using FluentAssertions; -using IdentityModel; -using UnitTests.Common; -using Microsoft.AspNetCore.Authentication; -using Xunit; - -namespace UnitTests.Extensions -{ - public class JwtPayloadCreationTests - { - private Token _token; - - public JwtPayloadCreationTests() - { - var claims = new List - { - new Claim(JwtClaimTypes.Scope, "scope1"), - new Claim(JwtClaimTypes.Scope, "scope2"), - new Claim(JwtClaimTypes.Scope, "scope3"), - }; - - _token = new Token(OidcConstants.TokenTypes.AccessToken) - { - CreationTime = DateTime.UtcNow, - Issuer = "issuer", - Lifetime = 60, - Claims = claims.Distinct(new ClaimComparer()).ToList(), - ClientId = "client" - }; - } - - [Fact] - public void Should_create_scopes_as_array_by_default() - { - var options = new IdentityServerOptions(); - var payload = _token.CreateJwtPayload(new SystemClock(), options, TestLogger.Create()); - - payload.Should().NotBeNull(); - var scopes = payload.Claims.Where(c => c.Type == JwtClaimTypes.Scope).ToArray(); - scopes.Count().Should().Be(3); - scopes[0].Value.Should().Be("scope1"); - scopes[1].Value.Should().Be("scope2"); - scopes[2].Value.Should().Be("scope3"); - } - - [Fact] - public void Should_create_scopes_as_string() - { - var options = new IdentityServerOptions - { - EmitScopesAsSpaceDelimitedStringInJwt = true - }; - - var payload = _token.CreateJwtPayload(new SystemClock(), options, TestLogger.Create()); - - payload.Should().NotBeNull(); - var scopes = payload.Claims.Where(c => c.Type == JwtClaimTypes.Scope).ToList(); - scopes.Count().Should().Be(1); - scopes.First().Value.Should().Be("scope1 scope2 scope3"); - } - } -} From 89fcfd19b38a6ad232ef6bf20ff428e1807dfc7e Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sun, 18 Oct 2020 15:38:59 +0200 Subject: [PATCH 23/25] log .net version at startup --- .../IdentityServerApplicationBuilderExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/IdentityServer/src/Configuration/IdentityServerApplicationBuilderExtensions.cs b/src/IdentityServer/src/Configuration/IdentityServerApplicationBuilderExtensions.cs index 5a8a70f44..999c8ab9e 100644 --- a/src/IdentityServer/src/Configuration/IdentityServerApplicationBuilderExtensions.cs +++ b/src/IdentityServer/src/Configuration/IdentityServerApplicationBuilderExtensions.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging; using System; using System.Reflection; +using System.Runtime.InteropServices; using System.Threading.Tasks; namespace Microsoft.AspNetCore.Builder @@ -54,7 +55,7 @@ internal static void Validate(this IApplicationBuilder app) if (loggerFactory == null) throw new ArgumentNullException(nameof(loggerFactory)); var logger = loggerFactory.CreateLogger("Duende.IdentityServer.Startup"); - logger.LogInformation("Starting Duende IdentityServer version {version}", typeof(Duende.IdentityServer.Hosting.IdentityServerMiddleware).Assembly.GetCustomAttribute().InformationalVersion); + logger.LogInformation("Starting Duende IdentityServer version {version} ({netversion})", typeof(Duende.IdentityServer.Hosting.IdentityServerMiddleware).Assembly.GetCustomAttribute().InformationalVersion, RuntimeInformation.FrameworkDescription); var scopeFactory = app.ApplicationServices.GetService(); From 00bff942c4fe4cb4c22446f9d03675432a39ac16 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sun, 18 Oct 2020 15:49:29 +0200 Subject: [PATCH 24/25] remove reference to newtonsoft json --- .../DependencyInjection/BuilderExtensions/Crypto.cs | 3 +-- src/IdentityServer/src/Duende.IdentityServer.csproj | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/IdentityServer/src/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs b/src/IdentityServer/src/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs index b8b74baf1..e0f292d1a 100644 --- a/src/IdentityServer/src/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs +++ b/src/IdentityServer/src/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs @@ -7,7 +7,6 @@ using Duende.IdentityServer.Models; using Duende.IdentityServer.Stores; using Microsoft.IdentityModel.Tokens; -using Newtonsoft.Json; using System; using System.IO; using System.Linq; @@ -185,7 +184,7 @@ public static IIdentityServerBuilder AddDeveloperSigningCredential( if (persistKey) { - File.WriteAllText(filename, JsonConvert.SerializeObject(jwk)); + File.WriteAllText(filename, System.Text.Json.JsonSerializer.Serialize(jwk)); } return builder.AddSigningCredential(key, signingAlgorithm); diff --git a/src/IdentityServer/src/Duende.IdentityServer.csproj b/src/IdentityServer/src/Duende.IdentityServer.csproj index 4d4a4029f..499d564de 100644 --- a/src/IdentityServer/src/Duende.IdentityServer.csproj +++ b/src/IdentityServer/src/Duende.IdentityServer.csproj @@ -16,8 +16,7 @@ - - + From 4e68669b00a45503615b6327eb16b7650cbb8638 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Wed, 21 Oct 2020 08:13:44 +0200 Subject: [PATCH 25/25] remove proxy launch profile --- src/IdentityServer/host/Properties/launchSettings.json | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/IdentityServer/host/Properties/launchSettings.json b/src/IdentityServer/host/Properties/launchSettings.json index 641abdaa2..c7cddae79 100644 --- a/src/IdentityServer/host/Properties/launchSettings.json +++ b/src/IdentityServer/host/Properties/launchSettings.json @@ -7,15 +7,6 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001;http://localhost:5000" - }, - "Host (proxy)": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "https://identityserver.local", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" } } } \ No newline at end of file