From fccf560443c0a10d733705af757572c7fc0e6423 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Thu, 11 Apr 2024 10:01:54 -0700 Subject: [PATCH 01/57] Only register IPrivateCertificateStore if it doesn't already exist. Allows a consumer to create their own more secure version of IPrivateCertificateStore. --- .../DependencyInjection/UdapBuilderExtensions/UdapCore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Udap.Server/Configuration/DependencyInjection/UdapBuilderExtensions/UdapCore.cs b/Udap.Server/Configuration/DependencyInjection/UdapBuilderExtensions/UdapCore.cs index 80673fed..7b980b0c 100644 --- a/Udap.Server/Configuration/DependencyInjection/UdapBuilderExtensions/UdapCore.cs +++ b/Udap.Server/Configuration/DependencyInjection/UdapBuilderExtensions/UdapCore.cs @@ -116,7 +116,7 @@ public static IUdapServiceBuilder AddUdapResponseGenerators(this IUdapServiceBui public static IUdapServiceBuilder AddPrivateFileStore(this IUdapServiceBuilder builder, string? resourceServerName = null) { - builder.Services.AddSingleton(sp => + builder.Services.TryAddSingleton(sp => new IssuedCertificateStore( sp.GetRequiredService>(), sp.GetRequiredService>(), From 28687e90b9f88a1822231e85a2936bd90d1b71e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:10:00 +0000 Subject: [PATCH 02/57] Bump xunit from 2.7.0 to 2.7.1 Bumps [xunit](https://github.com/xunit/xunit) from 2.7.0 to 2.7.1. - [Commits](https://github.com/xunit/xunit/compare/2.7.0...2.7.1) --- updated-dependencies: - dependency-name: xunit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- _tests/Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tests/Directory.Packages.props b/_tests/Directory.Packages.props index abdbeecf..52d3d9cb 100644 --- a/_tests/Directory.Packages.props +++ b/_tests/Directory.Packages.props @@ -28,7 +28,7 @@ - + From 9d40e720587fc726290aa4ef55e19135b654e895 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:16:05 +0000 Subject: [PATCH 03/57] Bump Udap.Client and Udap.Common Bumps [Udap.Client](https://github.com/JoeShook/udap-dotnet) and Udap.Common. These dependencies needed to be updated together. Updates `Udap.Client` from 0.3.35 to 0.3.37 - [Commits](https://github.com/JoeShook/udap-dotnet/compare/v0.3.35...v0.3.37) Updates `Udap.Common` from 0.3.35 to 0.3.37 --- updated-dependencies: - dependency-name: Udap.Client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Common dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj | 2 +- .../clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj index aeeb0228..97aa6e04 100644 --- a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj +++ b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + diff --git a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj index 470bcc4c..5bc77cc7 100644 --- a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj +++ b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + From 0aec312d70712141b6d97ca581090721bc2498b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:18:49 +0000 Subject: [PATCH 04/57] Bump Microsoft.AspNetCore.DataProtection.Abstractions Bumps [Microsoft.AspNetCore.DataProtection.Abstractions](https://github.com/dotnet/aspnetcore) from 8.0.2 to 8.0.4. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.2...v8.0.4) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.DataProtection.Abstractions dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index fe03489d..b4aa0e41 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,7 +11,7 @@ - + From 7b4e288b7e5243037765bafb9877bccec7e539c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:00:18 +0000 Subject: [PATCH 05/57] Bump OpenTelemetry.Instrumentation.Http Bumps [OpenTelemetry.Instrumentation.Http](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.7.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...Instrumentation.Http-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.Http dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index fe03489d..d7b9f79b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -43,7 +43,7 @@ - + From f6c4141150924a7a6805723fed83b5345a0f681a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:01:33 +0000 Subject: [PATCH 06/57] Bump OpenTelemetry.Instrumentation.Http in /examples/Udap.Auth.Server Bumps [OpenTelemetry.Instrumentation.Http](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.7.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...Instrumentation.Http-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.Http dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index fe03489d..d7b9f79b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -43,7 +43,7 @@ - + From 1e664db9b2de30a6d592143fc4190a9813032ea3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:01:40 +0000 Subject: [PATCH 07/57] Bump OpenTelemetry.Instrumentation.Http Bumps [OpenTelemetry.Instrumentation.Http](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.7.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...Instrumentation.Http-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.Http dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index fe03489d..d7b9f79b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -43,7 +43,7 @@ - + From 8f2702048ba62148ed07c69dc4c3eafdb85908eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:57:17 +0000 Subject: [PATCH 08/57] Bump OpenTelemetry.Instrumentation.Http from 1.7.0 to 1.8.1 Bumps [OpenTelemetry.Instrumentation.Http](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.7.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...Instrumentation.Http-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.Http dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index fe03489d..d7b9f79b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -43,7 +43,7 @@ - + From afe160666512df12f863159e84f47a242674afe1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 23:02:21 +0000 Subject: [PATCH 09/57] Bump System.CommandLine.Hosting Bumps [System.CommandLine.Hosting](https://github.com/dotnet/command-line-api) from 0.4.0-alpha.24126.1 to 0.4.0-alpha.24209.3. - [Release notes](https://github.com/dotnet/command-line-api/releases) - [Changelog](https://github.com/dotnet/command-line-api/blob/main/docs/History.md) - [Commits](https://github.com/dotnet/command-line-api/commits) --- updated-dependencies: - dependency-name: System.CommandLine.Hosting dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj | 2 +- .../clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj index 97aa6e04..a52d1fb5 100644 --- a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj +++ b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj @@ -36,7 +36,7 @@ - + diff --git a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj index 5bc77cc7..cbbf4318 100644 --- a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj +++ b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj @@ -36,7 +36,7 @@ - + From f57cba35e47aa03e4d07728ca8f546e52a6fd3d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 23:05:00 +0000 Subject: [PATCH 10/57] Bump xunit.runner.visualstudio from 2.5.7 to 2.5.8 Bumps [xunit.runner.visualstudio](https://github.com/xunit/visualstudio.xunit) from 2.5.7 to 2.5.8. - [Release notes](https://github.com/xunit/visualstudio.xunit/releases) - [Commits](https://github.com/xunit/visualstudio.xunit/compare/2.5.7...2.5.8) --- updated-dependencies: - dependency-name: xunit.runner.visualstudio dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- _tests/Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tests/Directory.Packages.props b/_tests/Directory.Packages.props index 52d3d9cb..535e20b9 100644 --- a/_tests/Directory.Packages.props +++ b/_tests/Directory.Packages.props @@ -29,7 +29,7 @@ - + From d1bff5b242bb192c42b4bdc8f6caee2d822abb7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 23:07:50 +0000 Subject: [PATCH 11/57] Bump OpenTelemetry.Instrumentation.AspNetCore Bumps [OpenTelemetry.Instrumentation.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.7.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...Instrumentation.Http-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.AspNetCore dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d7b9f79b..29022de6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -42,7 +42,7 @@ - + From 2d14cd75b1fb444937859fee807c908f4c7c1f2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 23:08:21 +0000 Subject: [PATCH 12/57] Bump Microsoft.AspNetCore.Authentication.OpenIdConnect Bumps [Microsoft.AspNetCore.Authentication.OpenIdConnect](https://github.com/dotnet/aspnetcore) from 8.0.2 to 8.0.4. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.2...v8.0.4) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.Authentication.OpenIdConnect dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index b4aa0e41..01f8ef5d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,7 +10,7 @@ - + From 7e07e598e5d69ac80563fe701612157663c3750a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 23:08:38 +0000 Subject: [PATCH 13/57] Bump OpenTelemetry.Instrumentation.AspNetCore Bumps [OpenTelemetry.Instrumentation.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.7.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...Instrumentation.Http-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.AspNetCore dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d7b9f79b..29022de6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -42,7 +42,7 @@ - + From 760587e6ab5c64edc98f55914994bfddcedbbe32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 23:08:58 +0000 Subject: [PATCH 14/57] Bump OpenTelemetry.Instrumentation.AspNetCore Bumps [OpenTelemetry.Instrumentation.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.7.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...Instrumentation.Http-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.AspNetCore dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d7b9f79b..29022de6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -42,7 +42,7 @@ - + From c7f3cc30ef2c7b03f4b706ee538d8566ed2da4e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 12:57:04 +0000 Subject: [PATCH 15/57] Bump OpenTelemetry.Instrumentation.AspNetCore from 1.7.0 to 1.8.1 Bumps [OpenTelemetry.Instrumentation.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.7.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...Instrumentation.Http-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.AspNetCore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ee14ccc3..1adf6ce3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -42,7 +42,7 @@ - + From 4cc9b8a4b69f38185a38e1cd05a34868d4be3f3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:04:39 +0000 Subject: [PATCH 16/57] Bump OpenTelemetry.Instrumentation.Http from 1.8.0 to 1.8.1 Bumps [OpenTelemetry.Instrumentation.Http](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.8.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.8.0...Instrumentation.Http-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.Http dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/Udap.Auth.Server/Udap.Auth.Server.csproj | 2 +- .../Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj | 2 +- examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index c7b08eb7..3d7a2a18 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -31,7 +31,7 @@ - + diff --git a/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj b/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj index 6901cf3a..ad9fc572 100644 --- a/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj +++ b/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj @@ -34,7 +34,7 @@ - + diff --git a/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj b/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj index d96e6578..e430f3b1 100644 --- a/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj +++ b/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj @@ -34,7 +34,7 @@ - + From 350f26a6d0412d58c88145818a2f449b0b996fed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:51:49 +0000 Subject: [PATCH 17/57] Bump Google.Apis.Auth from 1.67.0 to 1.68.0 Bumps [Google.Apis.Auth](https://github.com/googleapis/google-api-dotnet-client) from 1.67.0 to 1.68.0. - [Release notes](https://github.com/googleapis/google-api-dotnet-client/releases) - [Commits](https://github.com/googleapis/google-api-dotnet-client/commits) --- updated-dependencies: - dependency-name: Google.Apis.Auth dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 1adf6ce3..a3efe572 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,7 +5,7 @@ - + diff --git a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj index ebdb0d99..a7c55be3 100644 --- a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj +++ b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj @@ -12,7 +12,7 @@ - + From 64609aaefbc34b21d8cea5b5751d6e99e0afb998 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 12:45:50 +0000 Subject: [PATCH 18/57] Bump OpenTelemetry.Instrumentation.AspNetCore from 1.8.0 to 1.8.1 Bumps [OpenTelemetry.Instrumentation.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet) from 1.8.0 to 1.8.1. - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.8.0...core-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Instrumentation.AspNetCore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 10 +++++----- examples/Udap.Auth.Server/Udap.Auth.Server.csproj | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 1adf6ce3..acdae9a4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -30,7 +30,7 @@ - + @@ -38,10 +38,10 @@ - - - - + + + + diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index 3d7a2a18..503edfea 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -30,7 +30,7 @@ - + From 5b1f5a20d03ebfa76cf945d7f67ffb5200bb1227 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:16:10 +0000 Subject: [PATCH 19/57] Bump xunit from 2.7.1 to 2.8.0 Bumps [xunit](https://github.com/xunit/xunit) from 2.7.1 to 2.8.0. - [Commits](https://github.com/xunit/xunit/compare/2.7.1...2.8.0) --- updated-dependencies: - dependency-name: xunit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- _tests/Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tests/Directory.Packages.props b/_tests/Directory.Packages.props index 535e20b9..54bfa8a4 100644 --- a/_tests/Directory.Packages.props +++ b/_tests/Directory.Packages.props @@ -28,7 +28,7 @@ - + From 6719b3a097a99d08cd07062e01e978067d791c87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 22:28:35 +0000 Subject: [PATCH 20/57] Bump OpenTelemetry.Exporter.Console and OpenTelemetry Bumps [OpenTelemetry.Exporter.Console](https://github.com/open-telemetry/opentelemetry-dotnet) and [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-dotnet). These dependencies needed to be updated together. Updates `OpenTelemetry.Exporter.Console` from 1.7.0 to 1.8.1 - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...core-1.8.1) Updates `OpenTelemetry` from 1.7.0 to 1.8.1 - [Release notes](https://github.com/open-telemetry/opentelemetry-dotnet/releases) - [Commits](https://github.com/open-telemetry/opentelemetry-dotnet/compare/core-1.7.0...core-1.8.1) --- updated-dependencies: - dependency-name: OpenTelemetry.Exporter.Console dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: OpenTelemetry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 4 ++-- examples/Udap.Auth.Server/Udap.Auth.Server.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3876da70..759e5d76 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,8 +38,8 @@ - - + + diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index 503edfea..841165fd 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -26,8 +26,8 @@ - - + + From 9aa623ea68e390f07d54e45eafc7ed6b3dd31c81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 22:29:49 +0000 Subject: [PATCH 21/57] Bump xunit.runner.visualstudio from 2.5.8 to 2.8.0 Bumps [xunit.runner.visualstudio](https://github.com/xunit/visualstudio.xunit) from 2.5.8 to 2.8.0. - [Release notes](https://github.com/xunit/visualstudio.xunit/releases) - [Commits](https://github.com/xunit/visualstudio.xunit/compare/2.5.8...2.8.0) --- updated-dependencies: - dependency-name: xunit.runner.visualstudio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- _tests/Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tests/Directory.Packages.props b/_tests/Directory.Packages.props index 54bfa8a4..c5320d65 100644 --- a/_tests/Directory.Packages.props +++ b/_tests/Directory.Packages.props @@ -29,7 +29,7 @@ - + From 528bd820d5dfd31165c577274ccb95503e96e5b0 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Mon, 29 Apr 2024 15:39:27 -0700 Subject: [PATCH 22/57] Update packages --- Udap.sln | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Udap.sln b/Udap.sln index ce24285b..8d955d04 100644 --- a/Udap.sln +++ b/Udap.sln @@ -21,6 +21,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{522A1681-215E-4C39-B1AC-1324F7CE516E}" ProjectSection(SolutionItems) = preProject .dockerignore = .dockerignore + .gitignore = .gitignore Common.props = Common.props Directory.Build.props = Directory.Build.props Directory.Packages.props = Directory.Packages.props @@ -82,9 +83,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Udap.UI", "Udap.UI\Udap.UI. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Udap.Proxy.Server", "examples\Udap.Proxy.Server\Udap.Proxy.Server.csproj", "{BC032973-A216-483C-8C5B-C7B5D9EB0D19}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Udap.Smart.Metadata", "Udap.Smart.Metadata\Udap.Smart.Metadata.csproj", "{6E2FC3C1-53B0-46F6-981F-58AC96462F2F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Udap.Smart.Metadata", "Udap.Smart.Metadata\Udap.Smart.Metadata.csproj", "{6E2FC3C1-53B0-46F6-981F-58AC96462F2F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Udap.Smart.Model", "Udap.Smart.Model\Udap.Smart.Model.csproj", "{DD9B2367-11A6-448C-B733-F5F436A0AA87}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Udap.Smart.Model", "Udap.Smart.Model\Udap.Smart.Model.csproj", "{DD9B2367-11A6-448C-B733-F5F436A0AA87}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 21d301ea748abcaaeda73b61afffa8d16c475017 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Mon, 29 Apr 2024 15:52:33 -0700 Subject: [PATCH 23/57] Update packages --- examples/Udap.Auth.Server/Udap.Auth.Server.csproj | 4 ++-- .../Udap.Identity.Provider.2.csproj | 10 +++++----- .../Udap.Identity.Provider.csproj | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index 841165fd..ec645178 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -28,8 +28,8 @@ - - + + diff --git a/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj b/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj index ad9fc572..034da325 100644 --- a/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj +++ b/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj @@ -29,11 +29,11 @@ - - - - - + + + + + diff --git a/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj b/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj index e430f3b1..07e5ba39 100644 --- a/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj +++ b/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj @@ -29,11 +29,11 @@ - - - - - + + + + + From eda9f00aec9129a245a31875614b979cd1a2d800 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Tue, 30 Apr 2024 14:34:07 -0700 Subject: [PATCH 24/57] Validation enhancments Updating validation to check alg and x5c failures. Found that I had a fundamental error in that I was not configuring the TokeValidationParameters.VaidAlgorithms from a known list. Rather I was setting it from the incoming alg which could be null. This was bad. Also some changes in udap.org/TestTool could lead to removing the removing the ServerSupport setting in SeverSettings. Not sure if I can completely remove it yet. Seems it might have to stay because the TestTool registers with no scopes if I remember right. --- Udap.Server/Extensions/ClientExtensions.cs | 17 +- .../Hosting/UdapScopeEnrichmentMiddleware.cs | 2 +- .../Default/UdapJwtSecretValidator.cs | 57 ++-- .../Basic/ClientCredentialsUdapModeTests.cs | 304 +++++++++++++++++- .../Basic/ReplayRegistrationTests.cs | 2 - .../Basic/UdapForceStateParamFalseTests.cs | 1 - .../UdapResponseTypeResponseModeTests.cs | 1 - .../Udap.Auth.Server/Udap.Auth.Server.csproj | 15 +- .../appsettings.Production.json | 4 +- examples/Udap.Auth.Server/cloudbuild.yaml | 2 +- 10 files changed, 352 insertions(+), 53 deletions(-) diff --git a/Udap.Server/Extensions/ClientExtensions.cs b/Udap.Server/Extensions/ClientExtensions.cs index b48ec54f..5fbd9166 100644 --- a/Udap.Server/Extensions/ClientExtensions.cs +++ b/Udap.Server/Extensions/ClientExtensions.cs @@ -63,10 +63,13 @@ public static class ClientExtensions return null; } - public static Task> GetUdapKeysAsync(this ParsedSecret secret) + public static IEnumerable? GetUdapKeys(this ParsedSecret secret) { var jsonWebToken = new JsonWebToken(secret.Credential as string); - var x5cArray = jsonWebToken.GetHeaderValue>("x5c"); + if (!jsonWebToken.TryGetHeaderValue>("x5c", out var x5cArray)) + { + return null; + } var certificates = x5cArray .Select(s => new X509Certificate2(Convert.FromBase64String(s.ToString()))) @@ -81,13 +84,17 @@ public static Task> GetUdapKeysAsync(this ParsedSecret secret) }) .ToList(); - return Task.FromResult(certificates); + return certificates; } - public static X509Certificate2 GetUdapEndCertAsync(this ParsedSecret secret) + public static X509Certificate2? GetUdapEndCertAsync(this ParsedSecret secret) { var jsonWebToken = new JsonWebToken(secret.Credential as string); - var x5cArray = jsonWebToken.GetHeaderValue>("x5c"); + + if(!jsonWebToken.TryGetHeaderValue>("x5c", out var x5cArray)) + { + return null; + } return new X509Certificate2(Convert.FromBase64String(x5cArray.First())); } diff --git a/Udap.Server/Hosting/UdapScopeEnrichmentMiddleware.cs b/Udap.Server/Hosting/UdapScopeEnrichmentMiddleware.cs index 926ca09c..e0524ce1 100644 --- a/Udap.Server/Hosting/UdapScopeEnrichmentMiddleware.cs +++ b/Udap.Server/Hosting/UdapScopeEnrichmentMiddleware.cs @@ -43,7 +43,7 @@ public async Task Invoke( IClientStore clients, IIdentityServerInteractionService interactionService) { - if (_udapServerOptions.ServerSupport == ServerSupport.UDAP && + if ( context.Request.Path.Value != null && context.Request.Path.Value.Contains(Constants.ProtocolRoutePaths.Token)) { diff --git a/Udap.Server/Validation/Default/UdapJwtSecretValidator.cs b/Udap.Server/Validation/Default/UdapJwtSecretValidator.cs index f2baf89a..7b309b42 100644 --- a/Udap.Server/Validation/Default/UdapJwtSecretValidator.cs +++ b/Udap.Server/Validation/Default/UdapJwtSecretValidator.cs @@ -19,6 +19,7 @@ using Microsoft.IdentityModel.Tokens; using Udap.Common.Certificates; using Udap.Common.Extensions; +using Udap.Model; using Udap.Server.Configuration; using Udap.Server.Extensions; using Udap.Server.Storage.Stores; @@ -64,7 +65,6 @@ public UdapJwtSecretValidator( } - //Todo: Write replay unit tests //Todo: Write workflow diagrams to describe this process /// Validates a secret @@ -107,11 +107,11 @@ public async Task ValidateAsync(IEnumerable secr if (certChainList != null && !certChainList.Any()) { - _logger.LogError("There are no anchors available to validate client assertion."); + _logger.LogError($"There are no anchors available to validate client assertion for cient_id: {parsedSecret.Id}"); return fail; } - + var validAudiences = new[] { // token endpoint URL @@ -122,11 +122,10 @@ public async Task ValidateAsync(IEnumerable secr }.Distinct(); var tokenHandler = new JsonWebTokenHandler() { MaximumTokenSizeInBytes = _options.InputLengthRestrictions.Jwt }; - var jsonWebToken = tokenHandler.ReadJsonWebToken(jwtTokenString); - + var tokenValidationParameters = new TokenValidationParameters { - IssuerSigningKeys = await parsedSecret.GetUdapKeysAsync(), + IssuerSigningKeys = parsedSecret.GetUdapKeys(), ValidateIssuerSigningKey = true, ValidIssuer = parsedSecret.Id, @@ -138,47 +137,43 @@ public async Task ValidateAsync(IEnumerable secr RequireSignedTokens = true, RequireExpirationTime = true, - ValidAlgorithms = new[] { jsonWebToken!.GetHeaderValue(JwtHeaderParameterNames.Alg) }, + ValidAlgorithms = new[] { + UdapConstants.SupportedAlgorithm.RS256, UdapConstants.SupportedAlgorithm.RS384, + UdapConstants.SupportedAlgorithm.ES256, UdapConstants.SupportedAlgorithm.ES384 }, ClockSkew = TimeSpan.FromMinutes(5), - // ValidateSignatureLast = true + ValidateSignatureLast = true }; - - - if (_serverSettings.ServerSupport == ServerSupport.UDAP) - { - - tokenValidationParameters.IssuerValidator = (issuer, token, parameters) => - { - if (issuer != null && jsonWebToken.Claims.FirstOrDefault(c => c.Issuer == issuer) != null) - { - return issuer; - } - - return null; - }; - } - var result = tokenHandler.ValidateToken(jwtTokenString, tokenValidationParameters); if (!result.IsValid) { _logger.LogError(result.Exception, "JWT token validation error"); - + + var jsonWebToken = tokenHandler.ReadJsonWebToken(jwtTokenString); + + if (!jsonWebToken!.TryGetHeaderValue(JwtHeaderParameterNames.Alg, out string _)) + { + _logger.LogError($"Missing jwt x5c claim in header for client_id: {parsedSecret.Id}"); + } + + if (!jsonWebToken!.TryGetHeaderValue(JwtHeaderParameterNames.X5c, out string _)) + { + _logger.LogError($"Missing jwt x5c claim in header for client_id: {parsedSecret.Id}"); + } + return fail; } var jwtToken = (JsonWebToken)result.SecurityToken; - if (_serverSettings.ServerSupport == ServerSupport.Hl7SecurityIG) + + if (jwtToken.Subject != jwtToken.Issuer) { - if (jwtToken.Subject != jwtToken.Issuer) - { - _logger.LogError("Both 'sub' and 'iss' in the client assertion token must have a value of client_id."); - return fail; - } + _logger.LogError("Both 'sub' and 'iss' in the client assertion token must have a value of client_id."); + return fail; } diff --git a/_tests/UdapServer.Tests/Conformance/Basic/ClientCredentialsUdapModeTests.cs b/_tests/UdapServer.Tests/Conformance/Basic/ClientCredentialsUdapModeTests.cs index 8d617614..bf33a5c3 100644 --- a/_tests/UdapServer.Tests/Conformance/Basic/ClientCredentialsUdapModeTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Basic/ClientCredentialsUdapModeTests.cs @@ -13,14 +13,18 @@ using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Text.Json; +using System.Text.Json.Nodes; using Duende.IdentityServer.Models; +using Duende.IdentityServer.Validation; using FluentAssertions; using IdentityModel; using IdentityModel.Client; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json.Linq; using NSubstitute; using Udap.Client.Client; using Udap.Client.Client.Extensions; @@ -58,7 +62,6 @@ public ClientCredentialsUdapModeTests(ITestOutputHelper testOutputHelper) { services.AddSingleton(new ServerSettings { - ServerSupport = ServerSupport.UDAP, DefaultUserScopes = "udap", DefaultSystemScopes = "udap" }); @@ -237,6 +240,303 @@ public async Task GetAccessToken() } + [Fact] + public async Task GetAccessToken_Without_algorithm() + { + var clientCert = new X509Certificate2("CertStore/issued/fhirlabs.net.client.pfx", "udap-test"); + + var udapClient = _mockPipeline.Resolve(); + + // + // Typically the client would validate a server before proceeding to registration. + // + udapClient.UdapServerMetaData = new UdapMetadata(Substitute.For(), Substitute.For>()) + { RegistrationEndpoint = UdapAuthServerPipeline.RegistrationEndpoint }; + + var regDocumentResult = await udapClient.RegisterClientCredentialsClient( + clientCert, + "system/Patient.rs"); + + regDocumentResult.GetError().Should().BeNull(); + + // + // Get Access Token + // + var now = DateTime.UtcNow; + var jwtPayload = new JwtPayLoadExtension( + regDocumentResult.ClientId, + IdentityServerPipeline.TokenEndpoint, + new List() + { + new Claim(JwtClaimTypes.Subject, regDocumentResult.ClientId!), + new Claim(JwtClaimTypes.IssuedAt, EpochTime.GetIntDate(now.ToUniversalTime()).ToString(), + ClaimValueTypes.Integer), + new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId()), + // new Claim(UdapConstants.JwtClaimTypes.Extensions, BuildHl7B2BExtensions() ) //see http://hl7.org/fhir/us/udap-security/b2b.html#constructing-authentication-token + }, + now.ToUniversalTime(), + now.AddMinutes(5).ToUniversalTime() + ); + + var clientAssertion = + SignedSoftwareStatementBuilder + .Create(clientCert, jwtPayload) + .Build("RS384"); + + var jwt = new JsonWebToken(clientAssertion); + var jObject = JObject.Parse(Base64UrlEncoder.Decode(jwt.EncodedHeader)); + jObject.Remove(JwtHeaderParameterNames.Alg); + var header = Base64UrlEncoder.Encode(jObject.ToString()); + clientAssertion = $"{header}.{jwt.EncodedPayload}.{jwt.EncodedSignature}"; + var clientRequest = new UdapClientCredentialsTokenRequest + { + Address = IdentityServerPipeline.TokenEndpoint, + //ClientId = result.ClientId, we use Implicit ClientId in the iss claim + ClientAssertion = new ClientAssertion() + { + Type = OidcConstants.ClientAssertionTypes.JwtBearer, + Value = clientAssertion + }, + Udap = UdapConstants.UdapVersionsSupportedValue, + Scope = "system/Patient.rs" + }; + + + var tokenResponse = await _mockPipeline.BackChannelClient.UdapRequestClientCredentialsTokenAsync(clientRequest); + + tokenResponse.IsError.Should().BeTrue(); + tokenResponse.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); + tokenResponse.Error.Should().Be("invalid_client"); + tokenResponse.ErrorType.Should().Be(ResponseErrorType.Protocol); + } + + [Fact] + public async Task GetAccessToken_Without_x5c() + { + var clientCert = new X509Certificate2("CertStore/issued/fhirlabs.net.client.pfx", "udap-test"); + + var udapClient = _mockPipeline.Resolve(); + + // + // Typically the client would validate a server before proceeding to registration. + // + udapClient.UdapServerMetaData = new UdapMetadata(Substitute.For(), Substitute.For>()) + { RegistrationEndpoint = UdapAuthServerPipeline.RegistrationEndpoint }; + + var regDocumentResult = await udapClient.RegisterClientCredentialsClient( + clientCert, + "system/Patient.rs"); + + regDocumentResult.GetError().Should().BeNull(); + + // + // Get Access Token + // + var now = DateTime.UtcNow; + var jwtPayload = new JwtPayLoadExtension( + regDocumentResult.ClientId, + IdentityServerPipeline.TokenEndpoint, + new List() + { + new Claim(JwtClaimTypes.Subject, regDocumentResult.ClientId!), + new Claim(JwtClaimTypes.IssuedAt, EpochTime.GetIntDate(now.ToUniversalTime()).ToString(), + ClaimValueTypes.Integer), + new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId()), + // new Claim(UdapConstants.JwtClaimTypes.Extensions, BuildHl7B2BExtensions() ) //see http://hl7.org/fhir/us/udap-security/b2b.html#constructing-authentication-token + }, + now.ToUniversalTime(), + now.AddMinutes(5).ToUniversalTime() + ); + + var clientAssertion = + SignedSoftwareStatementBuilder + .Create(clientCert, jwtPayload) + .Build("RS384"); + + var jwt = new JsonWebToken(clientAssertion); + var jObject = JObject.Parse(Base64UrlEncoder.Decode(jwt.EncodedHeader)); + jObject.Remove(JwtHeaderParameterNames.X5c); + var header = Base64UrlEncoder.Encode(jObject.ToString()); + clientAssertion = $"{header}.{jwt.EncodedPayload}.{jwt.EncodedSignature}"; + var clientRequest = new UdapClientCredentialsTokenRequest + { + Address = IdentityServerPipeline.TokenEndpoint, + //ClientId = result.ClientId, we use Implicit ClientId in the iss claim + ClientAssertion = new ClientAssertion() + { + Type = OidcConstants.ClientAssertionTypes.JwtBearer, + Value = clientAssertion + }, + Udap = UdapConstants.UdapVersionsSupportedValue, + Scope = "system/Patient.rs" + }; + + var tokenResponse = await _mockPipeline.BackChannelClient.UdapRequestClientCredentialsTokenAsync(clientRequest); + + tokenResponse.IsError.Should().BeTrue(); + tokenResponse.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); + tokenResponse.Error.Should().Be("invalid_client"); + tokenResponse.ErrorType.Should().Be(ResponseErrorType.Protocol); + } + + //Sign with RS384 but set the header alg claim to RS256 + [Fact] + public async Task GetAccessToken_With_invalid_alg() + { + var clientCert = new X509Certificate2("CertStore/issued/fhirlabs.net.client.pfx", "udap-test"); + + var udapClient = _mockPipeline.Resolve(); + + // + // Typically the client would validate a server before proceeding to registration. + // + udapClient.UdapServerMetaData = new UdapMetadata(Substitute.For(), Substitute.For>()) + { RegistrationEndpoint = UdapAuthServerPipeline.RegistrationEndpoint }; + + var regDocumentResult = await udapClient.RegisterClientCredentialsClient( + clientCert, + "system/Patient.rs"); + + regDocumentResult.GetError().Should().BeNull(); + + // + // Get Access Token + // + var now = DateTime.UtcNow; + var jwtPayload = new JwtPayLoadExtension( + regDocumentResult.ClientId, + IdentityServerPipeline.TokenEndpoint, + new List() + { + new Claim(JwtClaimTypes.Subject, regDocumentResult.ClientId!), + new Claim(JwtClaimTypes.IssuedAt, EpochTime.GetIntDate(now.ToUniversalTime()).ToString(), + ClaimValueTypes.Integer), + new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId()), + // new Claim(UdapConstants.JwtClaimTypes.Extensions, BuildHl7B2BExtensions() ) //see http://hl7.org/fhir/us/udap-security/b2b.html#constructing-authentication-token + }, + now.ToUniversalTime(), + now.AddMinutes(5).ToUniversalTime() + ); + + var clientAssertion = + SignedSoftwareStatementBuilder + .Create(clientCert, jwtPayload) + .Build("RS384"); + + var jwt = new JsonWebToken(clientAssertion); + var jObject = JObject.Parse(Base64UrlEncoder.Decode(jwt.EncodedHeader)); + jObject.Property(JwtHeaderParameterNames.Alg)!.Value = "RS256"; // Does not match + + var header = Base64UrlEncoder.Encode(jObject.ToString()); + + var securityKey = new X509SecurityKey(clientCert); + var signingCredentials = new SigningCredentials(securityKey, "RS384"); + var encodedSignature = + JwtTokenUtilities.CreateEncodedSignature(string.Concat(header, ".", jwt.EncodedPayload), + signingCredentials); + + clientAssertion = $"{header}.{jwt.EncodedPayload}.{encodedSignature}"; + + var clientRequest = new UdapClientCredentialsTokenRequest + { + Address = IdentityServerPipeline.TokenEndpoint, + //ClientId = result.ClientId, we use Implicit ClientId in the iss claim + ClientAssertion = new ClientAssertion() + { + Type = OidcConstants.ClientAssertionTypes.JwtBearer, + Value = clientAssertion + }, + Udap = UdapConstants.UdapVersionsSupportedValue, + Scope = "system/Patient.rs" + }; + + + var tokenResponse = await _mockPipeline.BackChannelClient.UdapRequestClientCredentialsTokenAsync(clientRequest); + + tokenResponse.IsError.Should().BeTrue(); + tokenResponse.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); + tokenResponse.Error.Should().Be("invalid_client"); + tokenResponse.ErrorType.Should().Be(ResponseErrorType.Protocol); + } + + [Fact] + public async Task GetAccessToken_Without_iss() + { + var clientCert = new X509Certificate2("CertStore/issued/fhirlabs.net.client.pfx", "udap-test"); + + var udapClient = _mockPipeline.Resolve(); + + // + // Typically the client would validate a server before proceeding to registration. + // + udapClient.UdapServerMetaData = new UdapMetadata(Substitute.For(), Substitute.For>()) + { RegistrationEndpoint = UdapAuthServerPipeline.RegistrationEndpoint }; + + var regDocumentResult = await udapClient.RegisterClientCredentialsClient( + clientCert, + "system/Patient.rs"); + + regDocumentResult.GetError().Should().BeNull(); + + // + // Get Access Token + // + var now = DateTime.UtcNow; + var jwtPayload = new JwtPayLoadExtension( + regDocumentResult.ClientId, + IdentityServerPipeline.TokenEndpoint, + new List() + { + new Claim(JwtClaimTypes.Subject, regDocumentResult.ClientId!), + new Claim(JwtClaimTypes.IssuedAt, EpochTime.GetIntDate(now.ToUniversalTime()).ToString(), + ClaimValueTypes.Integer), + new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId()), + // new Claim(UdapConstants.JwtClaimTypes.Extensions, BuildHl7B2BExtensions() ) //see http://hl7.org/fhir/us/udap-security/b2b.html#constructing-authentication-token + }, + now.ToUniversalTime(), + now.AddMinutes(5).ToUniversalTime() + ); + + var clientAssertion = + SignedSoftwareStatementBuilder + .Create(clientCert, jwtPayload) + .Build("RS384"); + + var jwt = new JsonWebToken(clientAssertion); + var jObject = JObject.Parse(Base64UrlEncoder.Decode(jwt.EncodedPayload)); + jObject.Remove(JwtClaimTypes.Issuer); + var encodedPayload = Base64UrlEncoder.Encode(jObject.ToString()); + + var securityKey = new X509SecurityKey(clientCert); + var signingCredentials = new SigningCredentials(securityKey, "RS384"); + var encodedSignature = + JwtTokenUtilities.CreateEncodedSignature(string.Concat(jwt.EncodedHeader, ".", encodedPayload), + signingCredentials); + + clientAssertion = $"{jwt.EncodedHeader}.{encodedPayload}.{encodedSignature}"; + + + var clientRequest = new UdapClientCredentialsTokenRequest + { + Address = IdentityServerPipeline.TokenEndpoint, + //ClientId = result.ClientId, we use Implicit ClientId in the iss claim + ClientAssertion = new ClientAssertion() + { + Type = OidcConstants.ClientAssertionTypes.JwtBearer, + Value = clientAssertion + }, + Udap = UdapConstants.UdapVersionsSupportedValue, + Scope = "system/Patient.rs" + }; + + var tokenResponse = await _mockPipeline.BackChannelClient.UdapRequestClientCredentialsTokenAsync(clientRequest); + + tokenResponse.IsError.Should().BeTrue(); + tokenResponse.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); + tokenResponse.Error.Should().Be("invalid_client"); + tokenResponse.ErrorType.Should().Be(ResponseErrorType.Protocol); + } + [Fact] public async Task GetAccessTokenECDSA() { @@ -296,7 +596,7 @@ public async Task GetAccessTokenECDSA() }; var tokenResponse = await _mockPipeline.BackChannelClient.UdapRequestClientCredentialsTokenAsync(clientRequest); - + tokenResponse.Scope.Should().Be("system/Patient.rs", tokenResponse.Raw); } diff --git a/_tests/UdapServer.Tests/Conformance/Basic/ReplayRegistrationTests.cs b/_tests/UdapServer.Tests/Conformance/Basic/ReplayRegistrationTests.cs index da5d99da..29a88dc1 100644 --- a/_tests/UdapServer.Tests/Conformance/Basic/ReplayRegistrationTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Basic/ReplayRegistrationTests.cs @@ -45,7 +45,6 @@ public ReplayRegistrationTests(ITestOutputHelper testOutputHelper) { s.AddSingleton(new ServerSettings { - ServerSupport = ServerSupport.UDAP, DefaultUserScopes = "udap", DefaultSystemScopes = "udap", ForceStateParamOnAuthorizationCode = true @@ -64,7 +63,6 @@ public ReplayRegistrationTests(ITestOutputHelper testOutputHelper) { s.AddSingleton(new ServerSettings { - ServerSupport = ServerSupport.UDAP, DefaultUserScopes = "udap", DefaultSystemScopes = "udap", ForceStateParamOnAuthorizationCode = true, diff --git a/_tests/UdapServer.Tests/Conformance/Basic/UdapForceStateParamFalseTests.cs b/_tests/UdapServer.Tests/Conformance/Basic/UdapForceStateParamFalseTests.cs index e8175d43..32fab76a 100644 --- a/_tests/UdapServer.Tests/Conformance/Basic/UdapForceStateParamFalseTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Basic/UdapForceStateParamFalseTests.cs @@ -57,7 +57,6 @@ public UdapForceStateParamFalseTests(ITestOutputHelper testOutputHelper) { s.AddSingleton(new ServerSettings { - ServerSupport = ServerSupport.Hl7SecurityIG, DefaultUserScopes = "user/*.read", DefaultSystemScopes = "system/*.read", // ForceStateParamOnAuthorizationCode = false (default) diff --git a/_tests/UdapServer.Tests/Conformance/Basic/UdapResponseTypeResponseModeTests.cs b/_tests/UdapServer.Tests/Conformance/Basic/UdapResponseTypeResponseModeTests.cs index 8ceac467..56ff00bd 100644 --- a/_tests/UdapServer.Tests/Conformance/Basic/UdapResponseTypeResponseModeTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Basic/UdapResponseTypeResponseModeTests.cs @@ -57,7 +57,6 @@ public UdapResponseTypeResponseModeTests(ITestOutputHelper testOutputHelper) { s.AddSingleton(new ServerSettings { - ServerSupport = ServerSupport.Hl7SecurityIG, DefaultUserScopes = "user/*.read", DefaultSystemScopes = "system/*.read", ForceStateParamOnAuthorizationCode = true, diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index ec645178..3ae4dadd 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -37,15 +37,16 @@ - - - + + + + - - - - + + + + diff --git a/examples/Udap.Auth.Server/appsettings.Production.json b/examples/Udap.Auth.Server/appsettings.Production.json index b243ff0e..b9be8fd1 100644 --- a/examples/Udap.Auth.Server/appsettings.Production.json +++ b/examples/Udap.Auth.Server/appsettings.Production.json @@ -27,10 +27,10 @@ //https://hl7.org/fhir/smart-app-launch/scopes-and-launch-context.html "DefaultSystemScopes": "openid system/*.rs system/*.read", "DefaultUserScopes": "openid user/*.rs user/*/read", - "ForceStateParamOnAuthorizationCode": true + "ForceStateParamOnAuthorizationCode": true }, "ConnectionStrings": { - "DefaultConnection": "Data Source=host.docker.internal;Initial Catalog=Udap.Idp.db;User ID=udap_user;Password=udap_password1;TrustServerCertificate=True;" + "DefaultConnection": "Host=host.docker.internal;Port=5432;Database=Udap.Auth.db;Username=udap_user;Password=udap_password1" } } \ No newline at end of file diff --git a/examples/Udap.Auth.Server/cloudbuild.yaml b/examples/Udap.Auth.Server/cloudbuild.yaml index c391d111..a0019538 100644 --- a/examples/Udap.Auth.Server/cloudbuild.yaml +++ b/examples/Udap.Auth.Server/cloudbuild.yaml @@ -17,7 +17,7 @@ steps: '--image', 'us-west1-docker.pkg.dev/$PROJECT_ID/cloud-run-source-deploy/udap.auth.server:$TAG_NAME', '--max-instances', '1', '--concurrency', '5', - '--set-env-vars', 'GCLOUD_PROJECT=true,GCPDeploy=true,UdapIdpBaseUrl=https://securedcontrols.net,DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3SUPPORT=false', + '--set-env-vars', 'GCLOUD_PROJECT=true,GCPDeploy=true,UdapIdpBaseUrl=https://securedcontrols.net,DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3SUPPORT=false,proxy-hosts=35.212.149.197 idp1.securedcontrols.net;35.212.149.197 idp2.securedcontrols.net;35.212.149.197 udap.zimt.work', '--vpc-connector', 'alloydb-connector', '--vpc-egress', 'all-traffic', '--ingress', 'internal-and-cloud-load-balancing', From a92246a4ad4af665220808cce51ff9758a8aa0fd Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Tue, 30 Apr 2024 14:40:48 -0700 Subject: [PATCH 25/57] Fixup --- examples/Udap.Auth.Server/Udap.Auth.Server.csproj | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index 3ae4dadd..b26425ad 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -37,16 +37,15 @@ - - + - - - - + + + + From c406d74e1d960bfe27947f63761ff8802d54a2db Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Tue, 30 Apr 2024 15:25:40 -0700 Subject: [PATCH 26/57] Update Udap.Auth.Server.csproj --- examples/Udap.Auth.Server/Udap.Auth.Server.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index b26425ad..ec645178 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -38,8 +38,8 @@ - - + + From e2627c5a20c1432c8299d668d03076cef6969e56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 12:43:25 +0000 Subject: [PATCH 27/57] Bump Udap.Client and Udap.Common Bumps [Udap.Client](https://github.com/JoeShook/udap-dotnet) and Udap.Common. These dependencies needed to be updated together. Updates `Udap.Client` from 0.3.37 to 0.3.38 - [Commits](https://github.com/JoeShook/udap-dotnet/compare/v0.3.37...v0.3.38) Updates `Udap.Common` from 0.3.37 to 0.3.38 --- updated-dependencies: - dependency-name: Udap.Client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Common dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj | 2 +- .../clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj index a52d1fb5..09f24ed9 100644 --- a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj +++ b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + diff --git a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj index cbbf4318..5b6ca1e3 100644 --- a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj +++ b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + From 4d720ac6e1aa6da54c1a7fa83c599da5ba8bbebe Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Wed, 1 May 2024 10:38:35 -0700 Subject: [PATCH 28/57] Minor update to UseUdapMetadataServer Ensure that a prefix url parameter has the correct prefix and suffix slashes whether it is supplied or not an weather a prefix and suffix is supplied or not. --- .../DependencyInjection/ServiceCollectionExtensions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs b/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs index 35c00429..455df5f9 100644 --- a/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs +++ b/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs @@ -22,6 +22,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Net.Http; +using Hl7.Fhir.Utility; using Udap.Common; using Udap.Common.Certificates; using Udap.Common.Extensions; @@ -126,7 +127,7 @@ public static IApplicationBuilder UseUdapMetadataServer(this WebApplication app, { EnsureMvcControllerUnloads(app); - app.MapGet($"/{prefixRoute}{UdapConstants.Discovery.DiscoveryEndpoint}", + app.MapGet($"/{prefixRoute?.EnsureTrailingSlash().RemovePrefix("/")}{UdapConstants.Discovery.DiscoveryEndpoint}", async ( [FromServices] UdapMetaDataEndpoint endpoint, HttpContext httpContext, @@ -156,7 +157,7 @@ public static IApplicationBuilder UseUdapMetadataServer(this WebApplication app, public static IApplicationBuilder UseUdapMetadataServer(this IApplicationBuilder app, string? prefixRoute = null) { - app.Map($"/{prefixRoute}{UdapConstants.Discovery.DiscoveryEndpoint}", path => + app.Map($"/{prefixRoute?.EnsureTrailingSlash().RemovePrefix("/")}{UdapConstants.Discovery.DiscoveryEndpoint}", path => { path.Run(async ctx => { From 90281aa853ecd87538fedfd41599aa609c2d938b Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Wed, 1 May 2024 10:42:16 -0700 Subject: [PATCH 29/57] Proxy Server additions Rewrite url references in FHIR resources Add Security section to capability statement. May need to more work and packaging here but this is a POC at the moment. --- examples/Udap.Proxy.Server/Program.cs | 97 +++++++++++++++++-- .../Udap.Proxy.Server.csproj | 7 +- .../appsettings.Development.json | 15 ++- examples/Udap.Proxy.Server/appsettings.json | 11 ++- 4 files changed, 119 insertions(+), 11 deletions(-) diff --git a/examples/Udap.Proxy.Server/Program.cs b/examples/Udap.Proxy.Server/Program.cs index a7da6989..1247c713 100644 --- a/examples/Udap.Proxy.Server/Program.cs +++ b/examples/Udap.Proxy.Server/Program.cs @@ -7,6 +7,12 @@ using Yarp.ReverseProxy.Transforms; using Google.Apis.Auth.OAuth2; using Udap.Smart.Model; +using Hl7.Fhir.Rest; +using Hl7.Fhir.Serialization; +using Hl7.Fhir.Model; +using System; +using ZiggyCreatures.Caching.Fusion; +using Task = System.Threading.Tasks.Task; var builder = WebApplication.CreateBuilder(args); @@ -19,6 +25,14 @@ builder.Services.Configure(builder.Configuration.GetRequiredSection("SmartMetadata")); builder.Services.AddSmartMetadata(); builder.Services.AddUdapMetadataServer(builder.Configuration); +builder.Services.AddFusionCache() + .WithDefaultEntryOptions(new FusionCacheEntryOptions + { + Duration = TimeSpan.FromMinutes(10), + FactorySoftTimeout = TimeSpan.FromMilliseconds(100), + AllowTimedOutFactoryBackgroundCompletion = true, + FailSafeMaxDuration = TimeSpan.FromHours(12) + }); builder.Services.AddAuthentication(OidcConstants.AuthenticationSchemes.AuthorizationHeaderBearer) @@ -26,7 +40,7 @@ { options.Authority = builder.Configuration["Jwt:Authority"]; options.RequireHttpsMetadata = bool.Parse(builder.Configuration["Jwt:RequireHttpsMetadata"] ?? "true"); - + options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false @@ -45,12 +59,12 @@ .ConfigureHttpClient((context, handler) => { // this is required to decompress automatically. ******* troubleshooting only ******* - handler.AutomaticDecompression = System.Net.DecompressionMethods.All; + handler.AutomaticDecompression = System.Net.DecompressionMethods.All; }) .AddTransforms(builderContext => { // Conditionally add a transform for routes that require auth. - if (builderContext.Route.Metadata != null && + if (builderContext.Route.Metadata != null && (builderContext.Route.Metadata.ContainsKey("GCPKeyResolve") || builderContext.Route.Metadata.ContainsKey("AccessToken"))) { builderContext.AddRequestTransform(async context => @@ -66,7 +80,7 @@ // Use the default credentials. Primary usage: running in Cloud Run under a specific service account if (builderContext.Route.Metadata != null && (builderContext.Route.Metadata.TryGetValue("ADC", out string? adc))) { - if(adc == "True") + if (adc == "True") { builderContext.AddRequestTransform(async context => { @@ -76,6 +90,39 @@ }); } } + + builderContext.AddResponseTransform(async responseContext => + { + if (responseContext.HttpContext.Request.Path == "/fhir/r4/metadata") + { + responseContext.SuppressResponseBody = true; + var cache = responseContext.HttpContext.RequestServices.GetRequiredService(); + var bytes = await cache.GetOrSetAsync("metadata", _ => GetFhirMetadata(responseContext, builder)); + + // Change Content-Length to match the modified body, or remove it. + responseContext.HttpContext.Response.ContentLength = bytes?.Length; + // Response headers are copied before transforms are invoked, update any needed headers on the HttpContext.Response. + await responseContext.HttpContext.Response.Body.WriteAsync(bytes); + } + }); + + builderContext.AddResponseTransform(async responseContext => + { + if (responseContext.HttpContext.Request.Path != "/fhir/r4/metadata" && responseContext.HttpContext.Request.Path.StartsWithSegments("/fhir/r4/")); + { + var stream = await responseContext.ProxyResponse!.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(stream); + // TODO: size limits, timeouts + var body = await reader.ReadToEndAsync(); + responseContext.SuppressResponseBody = true; + + var finalBytes = Encoding.UTF8.GetBytes(body.Replace($"\"url\": \"{builder.Configuration["FhirUrlProxy:Back"]}", + $"\"url\": \"{builder.Configuration["FhirUrlProxy:Front"]}")); + responseContext.HttpContext.Response.ContentLength = finalBytes.Length; + + await responseContext.HttpContext.Response.Body.WriteAsync(finalBytes); + } + }); }); var app = builder.Build(); @@ -92,7 +139,7 @@ app.MapReverseProxy(); app.UseSmartMetadata(); -app.UseUdapMetadataServer(); +app.UseUdapMetadataServer("fhir/r4"); // Ensure metadata can only be called from this base URL. app.Run(); @@ -123,7 +170,7 @@ catch (Exception ex) { Console.WriteLine(ex); //todo: Logger - + return string.Empty; } @@ -133,4 +180,42 @@ async Task UdapMedatData(string s) { return s; +} + +async Task GetFhirMetadata(ResponseTransformContext responseTransformContext, + WebApplicationBuilder webApplicationBuilder) +{ + var stream = await responseTransformContext.ProxyResponse.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(stream); + var body = await reader.ReadToEndAsync(); + + if (!string.IsNullOrEmpty(body)) + { + var capStatement = await new FhirJsonParser().ParseAsync(body); + var securityComponent = new CapabilityStatement.SecurityComponent(); + + securityComponent.Service.Add( + new CodeableConcept("http://fhir.udap.org/CodeSystem/capability-rest-security-service", + "UDAP", + "OAuth2 using UDAP profile (see http://www.udap.org)")); + + // + // https://build.fhir.org/ig/HL7/fhir-extensions/StructureDefinition-oauth-uris.html + // + var oauthUrlExtensions = new Extension(); + var securityExtension = new Extension("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris", oauthUrlExtensions); + securityExtension.Extension.Add(new Extension() { Url = "token", Value = new FhirUri(webApplicationBuilder.Configuration["Jwt:Token"]) }); + securityExtension.Extension.Add(new Extension() { Url = "authorize", Value = new FhirUri(webApplicationBuilder.Configuration["Jwt:Authorize"]) }); + securityExtension.Extension.Add(new Extension() { Url = "register", Value = new FhirUri(webApplicationBuilder.Configuration["Jwt:Register"]) }); + securityExtension.Extension.Add(new Extension() { Url = "manage", Value = new FhirUri(webApplicationBuilder.Configuration["Jwt:Manage"]) }); + securityComponent.Extension.Add(securityExtension); + capStatement.Rest.First().Security = securityComponent; + + body = new FhirJsonSerializer().SerializeToString(capStatement); + var bytes = Encoding.UTF8.GetBytes(body); + + return bytes; + } + + return null; } \ No newline at end of file diff --git a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj index a7c55be3..3f540492 100644 --- a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj +++ b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - e695d088-90a4-4c0e-95da-9bb9610f64ec + Linux . false @@ -17,7 +17,9 @@ + + @@ -76,6 +78,9 @@ Always + + Always + diff --git a/examples/Udap.Proxy.Server/appsettings.Development.json b/examples/Udap.Proxy.Server/appsettings.Development.json index a503b841..81f1bd3b 100644 --- a/examples/Udap.Proxy.Server/appsettings.Development.json +++ b/examples/Udap.Proxy.Server/appsettings.Development.json @@ -3,8 +3,9 @@ "LogLevel": { "Default": "Information", "Microsoft": "Information", - "Yarp": "Information", - "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler": "Information" + "Yarp": "Trace", + "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler": "Information", + "Udap": "Trace" } }, @@ -99,9 +100,17 @@ "Jwt": { "Authority": "https://host.docker.internal:5002", - "RequireHttpsMetadata": true + "RequireHttpsMetadata": true, + "Token": "https://host.docker.internal:5002/connect/token", + "Authorize": "https://host.docker.internal:5002/connect/authorize", + "Register": "https://host.docker.internal:5002/connect/register", + "Manage": "https://host.docker.internal:5002/grants" }, + "FhirUrlProxy": { + "Back": "https://healthcare.googleapis.com", + "Front": "https://localhost:7074" + }, "UdapMetadataOptions": { "Enabled": true, diff --git a/examples/Udap.Proxy.Server/appsettings.json b/examples/Udap.Proxy.Server/appsettings.json index 37c26018..309f6ae8 100644 --- a/examples/Udap.Proxy.Server/appsettings.json +++ b/examples/Udap.Proxy.Server/appsettings.json @@ -5,5 +5,14 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + + "Kestrel": { + "Certificates": { + "Default": { + "Path": "host.docker.internal.pfx", + "Password": "udap-test" + } + } + } } From e427cb1dca0238efdb28b1efacbbb49a1fe1e747 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Wed, 1 May 2024 11:06:13 -0700 Subject: [PATCH 30/57] Fixup url maps --- examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj | 2 +- examples/Udap.Proxy.Server/appsettings.Development.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj index 3f540492..eac71b24 100644 --- a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj +++ b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - + e695d088-90a4-4c0e-95da-9bb9610f64ec Linux . false diff --git a/examples/Udap.Proxy.Server/appsettings.Development.json b/examples/Udap.Proxy.Server/appsettings.Development.json index 81f1bd3b..563541d9 100644 --- a/examples/Udap.Proxy.Server/appsettings.Development.json +++ b/examples/Udap.Proxy.Server/appsettings.Development.json @@ -108,8 +108,8 @@ }, "FhirUrlProxy": { - "Back": "https://healthcare.googleapis.com", - "Front": "https://localhost:7074" + "Back": "https://healthcare.googleapis.com/v1beta1/projects/udap-idp/locations/us-west1/datasets/gFhirLab/fhirStores/fhirlabs_open/fhir", + "Front": "https://localhost:7074/fhir/r4" }, "UdapMetadataOptions": { From 316a18bbc48b0c67d0760090c7f7dade3a5737d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 12:35:00 +0000 Subject: [PATCH 31/57] Bump Udap.Client and Udap.Common Bumps [Udap.Client](https://github.com/JoeShook/udap-dotnet) and Udap.Common. These dependencies needed to be updated together. Updates `Udap.Client` from 0.3.38 to 0.3.39 - [Commits](https://github.com/JoeShook/udap-dotnet/compare/v0.3.38...v0.3.39) Updates `Udap.Common` from 0.3.38 to 0.3.39 --- updated-dependencies: - dependency-name: Udap.Client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Common dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj | 2 +- .../clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj index 09f24ed9..7f8401b6 100644 --- a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj +++ b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + diff --git a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj index 5b6ca1e3..47a27b83 100644 --- a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj +++ b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + From 93104dcafacb146bab8b76a2f12206e862d40612 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Fri, 3 May 2024 17:05:17 -0700 Subject: [PATCH 32/57] wip --- .../ServiceCollectionExtensions.cs | 1 - examples/Udap.Proxy.Server/Program.cs | 24 ++++++++++++++++--- .../Udap.Proxy.Server.csproj | 2 ++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs b/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs index 455df5f9..fc48aae5 100644 --- a/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs +++ b/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs @@ -21,7 +21,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System.Net.Http; using Hl7.Fhir.Utility; using Udap.Common; using Udap.Common.Certificates; diff --git a/examples/Udap.Proxy.Server/Program.cs b/examples/Udap.Proxy.Server/Program.cs index 1247c713..a3cf61ac 100644 --- a/examples/Udap.Proxy.Server/Program.cs +++ b/examples/Udap.Proxy.Server/Program.cs @@ -11,8 +11,11 @@ using Hl7.Fhir.Serialization; using Hl7.Fhir.Model; using System; +using Microsoft.IdentityModel.JsonWebTokens; using ZiggyCreatures.Caching.Fusion; using Task = System.Threading.Tasks.Task; +using System.IdentityModel.Tokens.Jwt; +using Udap.Util.Extensions; var builder = WebApplication.CreateBuilder(args); @@ -69,11 +72,26 @@ { builderContext.AddRequestTransform(async context => { - context.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await ResolveAccessToken(builderContext.Route.Metadata)); + var resolveAccessToken = await ResolveAccessToken(builderContext.Route.Metadata); + context.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", resolveAccessToken); + + + var tokenHandler = new JwtSecurityTokenHandler(); + var jsonToken = tokenHandler.ReadJwtToken(context.HttpContext.Request.Headers.Authorization.ToString().Replace("Bearer", "").Trim()); + var scopes = jsonToken?.Claims.Where(c => c.Type == "scope"); + var iss = jsonToken.Claims.Where(c => c.Type == "iss"); + // var sub = jsonToken.Claims.Where(c => c.Type == "sub"); // figure out what subject should be for GCP // Google Cloud way of passing scopes to the Fhir Server - // context.ProxyRequest.Headers.Add("X-Authorization-Scope", "user/Patient.read launch/patient"); - // context.ProxyRequest.Headers.Add("X-Authorization-Issuer", "securedcontrols.net"); + var spaceSeparatedString = scopes?.Select(s => s.Value) + .Where(s => s != "udap") //gcp doesn't know udap Need better filter to block unknown scopes + .ToSpaceSeparatedString(); + //logger + Console.WriteLine(spaceSeparatedString); + + context.ProxyRequest.Headers.Add("X-Authorization-Scope", spaceSeparatedString); + context.ProxyRequest.Headers.Add("X-Authorization-Issuer", iss.SingleOrDefault().Value); + // context.ProxyRequest.Headers.Add("X-Authorization-Subject", sub.SingleOrDefault().Value); }); } diff --git a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj index eac71b24..509e0d54 100644 --- a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj +++ b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj @@ -25,11 +25,13 @@ + + From a3cc78bd68db55caed91264a778c75cfab49fcad Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Sat, 4 May 2024 11:46:32 -0700 Subject: [PATCH 33/57] fixup metadata mapping bug and proxy server routes --- .../ServiceCollectionExtensions.cs | 4 +- Udap.Metadata.Server/UdapMetaDataEndpoint.cs | 1 - .../IdServerRegistrationTests.cs | 9 +-- examples/Udap.Proxy.Server/Program.cs | 63 ++++++++++++------- .../appsettings.Development.json | 27 +++++--- 5 files changed, 66 insertions(+), 38 deletions(-) diff --git a/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs b/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs index fc48aae5..37b8968f 100644 --- a/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs +++ b/Udap.Metadata.Server/Configuration/DependencyInjection/ServiceCollectionExtensions.cs @@ -136,13 +136,13 @@ public static IApplicationBuilder UseUdapMetadataServer(this WebApplication app, .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status404NotFound); // community doesn't exist - app.MapGet($"/{prefixRoute}{UdapConstants.Discovery.DiscoveryEndpoint}/communities", + app.MapGet($"/{prefixRoute?.EnsureTrailingSlash().RemovePrefix("/")}{UdapConstants.Discovery.DiscoveryEndpoint}/communities", ([FromServices] UdapMetaDataEndpoint endpoint) => endpoint.GetCommunities()) .AllowAnonymous() .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status404NotFound); // community doesn't exist - app.MapGet($"/{prefixRoute}{UdapConstants.Discovery.DiscoveryEndpoint}/communities/ashtml", + app.MapGet($"/{prefixRoute?.EnsureTrailingSlash().RemovePrefix("/")}{UdapConstants.Discovery.DiscoveryEndpoint}/communities/ashtml", ( [FromServices] UdapMetaDataEndpoint endpoint, HttpContext httpContext) => endpoint.GetCommunitiesAsHtml(httpContext)) diff --git a/Udap.Metadata.Server/UdapMetaDataEndpoint.cs b/Udap.Metadata.Server/UdapMetaDataEndpoint.cs index 8f5bd940..066aa3aa 100644 --- a/Udap.Metadata.Server/UdapMetaDataEndpoint.cs +++ b/Udap.Metadata.Server/UdapMetaDataEndpoint.cs @@ -42,7 +42,6 @@ public IResult GetCommunities() return Results.Ok(_metaDataBuilder.GetCommunities()); } - public IResult GetCommunitiesAsHtml(HttpContext httpContext) { var html = _metaDataBuilder.GetCommunitiesAsHtml(httpContext.Request.GetDisplayUrl().GetBaseUrlFromMetadataUrl()); diff --git a/_tests/Udap.Client.System.Tests/IdServerRegistrationTests.cs b/_tests/Udap.Client.System.Tests/IdServerRegistrationTests.cs index f4652467..c5a465d5 100644 --- a/_tests/Udap.Client.System.Tests/IdServerRegistrationTests.cs +++ b/_tests/Udap.Client.System.Tests/IdServerRegistrationTests.cs @@ -1187,9 +1187,10 @@ public async Task RegistrationSuccess_authorization_code_FhirLabs_desktop_Test() "mailto:Joseph.Shook@Surescripts.com", "mailto:JoeShook@gmail.com" }) .WithTokenEndpointAuthMethod(UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue) - // .WithScope("user/Patient.* user/Practitioner.read") //Comment out for UDAP Server mode. + .WithScope("user/Patient.read") //Comment out for UDAP Server mode. .WithResponseTypes(new HashSet { "code" }) .WithRedirectUrls(new List { new Uri($"https://client.fhirlabs.net/redirect/{Guid.NewGuid()}").AbsoluteUri }!) + .WithLogoUri("https://avatars.githubusercontent.com/u/77421324?s=48&v=4") .Build(); @@ -1215,7 +1216,7 @@ public async Task RegistrationSuccess_authorization_code_FhirLabs_desktop_Test() using var idpClient = new HttpClient(); // New client. The existing HttpClient chains up to a CustomTrustStore var response = await idpClient.PostAsJsonAsync(reg, requestBody); - response.StatusCode.Should().Be(HttpStatusCode.Created); + response.StatusCode.Should().BeOneOf(HttpStatusCode.Created, HttpStatusCode.OK); response.Content.Headers.ContentType!.ToString().Should().Be("application/json"); // var documentAsJson = JsonSerializer.Serialize(document); @@ -1295,7 +1296,7 @@ public async Task RegistrationSuccess_authorization_code_FhirLabs_desktop_Test() clientId: result.ClientId!, responseType: "code", state: CryptoRandom.CreateUniqueId(), - scope: "udap user.cruds", + scope: "user/Patient.read", redirectUri: document.RedirectUris!.First()); _testOutputHelper.WriteLine(url); @@ -1308,7 +1309,7 @@ public async Task RegistrationSuccess_authorization_code_FhirLabs_desktop_Test() response.StatusCode.Should().Be(HttpStatusCode.Redirect); var authUri = new Uri(disco.AuthorizeEndpoint!); - var loginUrl = $"{authUri.Scheme}://{authUri.Authority}/Account/Login"; + var loginUrl = $"{authUri.Scheme}://{authUri.Authority}/udapaccount/login"; response.Headers.Location?.ToString().Should() .StartWith(loginUrl); diff --git a/examples/Udap.Proxy.Server/Program.cs b/examples/Udap.Proxy.Server/Program.cs index a3cf61ac..f2447afa 100644 --- a/examples/Udap.Proxy.Server/Program.cs +++ b/examples/Udap.Proxy.Server/Program.cs @@ -15,6 +15,9 @@ using ZiggyCreatures.Caching.Fusion; using Task = System.Threading.Tasks.Task; using System.IdentityModel.Tokens.Jwt; +using Microsoft.AspNetCore.Mvc; +using Udap.Metadata.Server; +using Udap.Model; using Udap.Util.Extensions; var builder = WebApplication.CreateBuilder(args); @@ -76,22 +79,7 @@ context.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", resolveAccessToken); - var tokenHandler = new JwtSecurityTokenHandler(); - var jsonToken = tokenHandler.ReadJwtToken(context.HttpContext.Request.Headers.Authorization.ToString().Replace("Bearer", "").Trim()); - var scopes = jsonToken?.Claims.Where(c => c.Type == "scope"); - var iss = jsonToken.Claims.Where(c => c.Type == "iss"); - // var sub = jsonToken.Claims.Where(c => c.Type == "sub"); // figure out what subject should be for GCP - - // Google Cloud way of passing scopes to the Fhir Server - var spaceSeparatedString = scopes?.Select(s => s.Value) - .Where(s => s != "udap") //gcp doesn't know udap Need better filter to block unknown scopes - .ToSpaceSeparatedString(); - //logger - Console.WriteLine(spaceSeparatedString); - - context.ProxyRequest.Headers.Add("X-Authorization-Scope", spaceSeparatedString); - context.ProxyRequest.Headers.Add("X-Authorization-Issuer", iss.SingleOrDefault().Value); - // context.ProxyRequest.Headers.Add("X-Authorization-Subject", sub.SingleOrDefault().Value); + SetProxyHeaders(context); }); } @@ -116,23 +104,20 @@ responseContext.SuppressResponseBody = true; var cache = responseContext.HttpContext.RequestServices.GetRequiredService(); var bytes = await cache.GetOrSetAsync("metadata", _ => GetFhirMetadata(responseContext, builder)); - + // Change Content-Length to match the modified body, or remove it. responseContext.HttpContext.Response.ContentLength = bytes?.Length; + // Response headers are copied before transforms are invoked, update any needed headers on the HttpContext.Response. await responseContext.HttpContext.Response.Body.WriteAsync(bytes); } - }); - - builderContext.AddResponseTransform(async responseContext => - { - if (responseContext.HttpContext.Request.Path != "/fhir/r4/metadata" && responseContext.HttpContext.Request.Path.StartsWithSegments("/fhir/r4/")); + else if (responseContext.HttpContext.Request.Path.StartsWithSegments("/fhir/r4/")) { + responseContext.SuppressResponseBody = true; var stream = await responseContext.ProxyResponse!.Content.ReadAsStreamAsync(); using var reader = new StreamReader(stream); // TODO: size limits, timeouts var body = await reader.ReadToEndAsync(); - responseContext.SuppressResponseBody = true; var finalBytes = Encoding.UTF8.GetBytes(body.Replace($"\"url\": \"{builder.Configuration["FhirUrlProxy:Back"]}", $"\"url\": \"{builder.Configuration["FhirUrlProxy:Front"]}")); @@ -236,4 +221,36 @@ async Task UdapMedatData(string s) } return null; +} + +void SetProxyHeaders(RequestTransformContext requestTransformContext) +{ + if (!requestTransformContext.HttpContext.Request.Headers.Authorization.Any()) + { + return; + } + + var bearerToken = requestTransformContext.HttpContext.Request.Headers.Authorization.First(); + + if (bearerToken == null) + { + return; + } + + var tokenHandler = new JwtSecurityTokenHandler(); + var jsonToken = tokenHandler.ReadJwtToken(requestTransformContext.HttpContext.Request.Headers.Authorization.First()?.Replace("Bearer", "").Trim()); + var scopes = jsonToken?.Claims.Where(c => c.Type == "scope"); + var iss = jsonToken.Claims.Where(c => c.Type == "iss"); + // var sub = jsonToken.Claims.Where(c => c.Type == "sub"); // figure out what subject should be for GCP + + // Google Cloud way of passing scopes to the Fhir Server + var spaceSeparatedString = scopes?.Select(s => s.Value) + .Where(s => s != "udap") //gcp doesn't know udap Need better filter to block unknown scopes + .ToSpaceSeparatedString(); + //logger + Console.WriteLine(spaceSeparatedString); + + requestTransformContext.ProxyRequest.Headers.Add("X-Authorization-Scope", spaceSeparatedString); + requestTransformContext.ProxyRequest.Headers.Add("X-Authorization-Issuer", iss.SingleOrDefault().Value); + // context.ProxyRequest.Headers.Add("X-Authorization-Subject", sub.SingleOrDefault().Value); } \ No newline at end of file diff --git a/examples/Udap.Proxy.Server/appsettings.Development.json b/examples/Udap.Proxy.Server/appsettings.Development.json index 563541d9..6a220d0f 100644 --- a/examples/Udap.Proxy.Server/appsettings.Development.json +++ b/examples/Udap.Proxy.Server/appsettings.Development.json @@ -33,17 +33,28 @@ } ] }, - "gFhirLab-route-wellknown": { + + "gFhirLab-route-wellknown-udap": { "ClusterId": "self", "Match": { - "Path": "/fhir/r4/.well-known/{**remainder}" - }, - "Transforms": [ - { - "PathPattern": ".well-known/{**remainder}" - } - ] + "Path": "/fhir/r4/.well-known/udap" + } + }, + + "gFhirLab-route-wellknown-udap-commnities": { + "ClusterId": "self", + "Match": { + "Path": "/fhir/r4/.well-known/udap/communities" + } }, + + "gFhirLab-route-wellknown-udap-communities-ashtml": { + "ClusterId": "self", + "Match": { + "Path": "/fhir/r4/.well-known/udap/communities/ashtml" + } + }, + "gFhirLab-route-base": { "ClusterId": "gFhirLab-cluster", "MetaData": { From d3c288b78acc6c5bccd313d9831c7388749c618b Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Sun, 5 May 2024 11:19:08 -0700 Subject: [PATCH 34/57] fixup segment match --- examples/Udap.Proxy.Server/Program.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/Udap.Proxy.Server/Program.cs b/examples/Udap.Proxy.Server/Program.cs index f2447afa..54ea49c4 100644 --- a/examples/Udap.Proxy.Server/Program.cs +++ b/examples/Udap.Proxy.Server/Program.cs @@ -111,7 +111,8 @@ // Response headers are copied before transforms are invoked, update any needed headers on the HttpContext.Response. await responseContext.HttpContext.Response.Body.WriteAsync(bytes); } - else if (responseContext.HttpContext.Request.Path.StartsWithSegments("/fhir/r4/")) + else if (responseContext.HttpContext.Request.Path.HasValue && + responseContext.HttpContext.Request.Path.Value.StartsWith("/fhir/r4/", StringComparison.OrdinalIgnoreCase)) { responseContext.SuppressResponseBody = true; var stream = await responseContext.ProxyResponse!.Content.ReadAsStreamAsync(); @@ -122,7 +123,7 @@ var finalBytes = Encoding.UTF8.GetBytes(body.Replace($"\"url\": \"{builder.Configuration["FhirUrlProxy:Back"]}", $"\"url\": \"{builder.Configuration["FhirUrlProxy:Front"]}")); responseContext.HttpContext.Response.ContentLength = finalBytes.Length; - + await responseContext.HttpContext.Response.Body.WriteAsync(finalBytes); } }); From 25b05fdebacc63b30205b98b6035083db62ef11c Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Sun, 5 May 2024 14:58:15 -0700 Subject: [PATCH 35/57] wip set proxy headers --- examples/Udap.Proxy.Server/Program.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/examples/Udap.Proxy.Server/Program.cs b/examples/Udap.Proxy.Server/Program.cs index 54ea49c4..574b0206 100644 --- a/examples/Udap.Proxy.Server/Program.cs +++ b/examples/Udap.Proxy.Server/Program.cs @@ -78,7 +78,6 @@ var resolveAccessToken = await ResolveAccessToken(builderContext.Route.Metadata); context.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", resolveAccessToken); - SetProxyHeaders(context); }); } @@ -86,13 +85,15 @@ // Use the default credentials. Primary usage: running in Cloud Run under a specific service account if (builderContext.Route.Metadata != null && (builderContext.Route.Metadata.TryGetValue("ADC", out string? adc))) { - if (adc == "True") + if (adc.Equals("True", StringComparison.OrdinalIgnoreCase)) { builderContext.AddRequestTransform(async context => { var googleCredentials = GoogleCredential.GetApplicationDefault(); string accessToken = await googleCredentials.UnderlyingCredential.GetAccessTokenForRequestAsync(); context.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + + SetProxyHeaders(context); }); } } @@ -237,7 +238,12 @@ void SetProxyHeaders(RequestTransformContext requestTransformContext) { return; } - + + foreach (var requestHeader in requestTransformContext.HttpContext.Request.Headers) + { + Console.WriteLine(requestHeader.Value); + } + var tokenHandler = new JwtSecurityTokenHandler(); var jsonToken = tokenHandler.ReadJwtToken(requestTransformContext.HttpContext.Request.Headers.Authorization.First()?.Replace("Bearer", "").Trim()); var scopes = jsonToken?.Claims.Where(c => c.Type == "scope"); @@ -248,9 +254,7 @@ void SetProxyHeaders(RequestTransformContext requestTransformContext) var spaceSeparatedString = scopes?.Select(s => s.Value) .Where(s => s != "udap") //gcp doesn't know udap Need better filter to block unknown scopes .ToSpaceSeparatedString(); - //logger - Console.WriteLine(spaceSeparatedString); - + requestTransformContext.ProxyRequest.Headers.Add("X-Authorization-Scope", spaceSeparatedString); requestTransformContext.ProxyRequest.Headers.Add("X-Authorization-Issuer", iss.SingleOrDefault().Value); // context.ProxyRequest.Headers.Add("X-Authorization-Subject", sub.SingleOrDefault().Value); From 20aa8edd267fbad693939bf91c31a92a0fe08195 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 13:09:17 +0000 Subject: [PATCH 36/57] Bump Udap.Client, Udap.Common and Udap.Util Bumps [Udap.Client](https://github.com/JoeShook/udap-dotnet), Udap.Common and Udap.Util. These dependencies needed to be updated together. Updates `Udap.Client` from 0.3.39 to 0.3.40 - [Commits](https://github.com/JoeShook/udap-dotnet/compare/v0.3.39...v0.3.40) Updates `Udap.Common` from 0.3.39 to 0.3.40 Updates `Udap.Util` from 0.3.39 to 0.3.40 --- updated-dependencies: - dependency-name: Udap.Client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Common dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Util dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj | 2 +- .../clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj index 7f8401b6..e692fc87 100644 --- a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj +++ b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + diff --git a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj index 47a27b83..2ec14813 100644 --- a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj +++ b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + From 5c520d63ca72c2d38a5fcc68c6d86ea3d57def7e Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Mon, 6 May 2024 11:54:35 -0700 Subject: [PATCH 37/57] Remove legacy modes Udap.org/TestTool has been updated and legacy modes no longer needed. --- ...TokenRequestForAuthorizationCodeBuilder.cs | 48 +++++------------- ...TokenRequestForClientCredentialsBuilder.cs | 50 +++++-------------- 2 files changed, 25 insertions(+), 73 deletions(-) diff --git a/Udap.Model/Access/AccessTokenRequestForAuthorizationCodeBuilder.cs b/Udap.Model/Access/AccessTokenRequestForAuthorizationCodeBuilder.cs index f3e9a2d3..3ed62afa 100644 --- a/Udap.Model/Access/AccessTokenRequestForAuthorizationCodeBuilder.cs +++ b/Udap.Model/Access/AccessTokenRequestForAuthorizationCodeBuilder.cs @@ -67,21 +67,13 @@ public AccessTokenRequestForAuthorizationCodeBuilder WithClaim(Claim claim) } /// - /// Legacy refers to the current udap.org/UDAPTestTool behavior as documented in - /// udap.org profiles. The HL7 Security IG has the following constraint to make it - /// more friendly with OIDC and SMART launch frameworks. - /// sub == iss == client_id - /// Where as the Legacy is the following behavior - /// sub == iis == SubAlt Name + /// Build an /// - /// /// /// - public UdapAuthorizationCodeTokenRequest Build( - bool legacy = false, - string? algorithm = UdapConstants.SupportedAlgorithm.RS256) + public UdapAuthorizationCodeTokenRequest Build(string? algorithm = UdapConstants.SupportedAlgorithm.RS256) { - var clientAssertion = BuildClientAssertion(algorithm, legacy); + var clientAssertion = BuildClientAssertion(algorithm); return new UdapAuthorizationCodeTokenRequest() { @@ -99,34 +91,18 @@ public UdapAuthorizationCodeTokenRequest Build( }; } - private string? BuildClientAssertion(string algorithm, bool legacy = false) + private string? BuildClientAssertion(string algorithm) { JwtPayLoadExtension jwtPayload; - if (legacy) - { - //udap.org profile - jwtPayload = new JwtPayLoadExtension( - _certificate.GetNameInfo(X509NameType.UrlName, - false), //TODO:: Let user pick the subject alt name. Create will need extra param. - _tokenEndpoint, //The FHIR Authorization Server's token endpoint URL - _claims, - _now, - _now.AddMinutes(5) - ); - } - - else - { - //HL7 FHIR IG profile - jwtPayload = new JwtPayLoadExtension( - _clientId, - _tokenEndpoint, //The FHIR Authorization Server's token endpoint URL - _claims, - _now, - _now.AddMinutes(5) - ); - } + //HL7 FHIR IG profile + jwtPayload = new JwtPayLoadExtension( + _clientId, + _tokenEndpoint, //The FHIR Authorization Server's token endpoint URL + _claims, + _now, + _now.AddMinutes(5) + ); return SignedSoftwareStatementBuilder .Create(_certificate, jwtPayload) diff --git a/Udap.Model/Access/AccessTokenRequestForClientCredentialsBuilder.cs b/Udap.Model/Access/AccessTokenRequestForClientCredentialsBuilder.cs index 3df21b85..e9eb483d 100644 --- a/Udap.Model/Access/AccessTokenRequestForClientCredentialsBuilder.cs +++ b/Udap.Model/Access/AccessTokenRequestForClientCredentialsBuilder.cs @@ -91,21 +91,13 @@ public AccessTokenRequestForClientCredentialsBuilder WithExtension(string key, B } /// - /// Legacy refers to the current udap.org/UDAPTestTool behavior as documented in - /// udap.org profiles. The HL7 Security IG has the following constraint to make it - /// more friendly with OIDC and SMART launch frameworks. - /// sub == iss == client_id - /// Where as the Legacy is the following behavior - /// sub == iis == SubAlt Name + /// Build an /// - /// /// /// - public UdapClientCredentialsTokenRequest Build( - bool legacy = false, - string? algorithm = UdapConstants.SupportedAlgorithm.RS256) + public UdapClientCredentialsTokenRequest Build(string? algorithm = UdapConstants.SupportedAlgorithm.RS256) { - var clientAssertion = BuildClientAssertion(algorithm, legacy); + var clientAssertion = BuildClientAssertion(algorithm); return new UdapClientCredentialsTokenRequest { @@ -122,34 +114,18 @@ public UdapClientCredentialsTokenRequest Build( } - private string BuildClientAssertion(string algorithm, bool legacy = false) + private string BuildClientAssertion(string algorithm) { JwtPayLoadExtension jwtPayload; - - if (legacy) - { - //udap.org profile - jwtPayload = new JwtPayLoadExtension( - _certificate.GetNameInfo(X509NameType.UrlName, - false), //TODO:: Let user pick the subject alt name. Create will need extra param. - _tokenEndoint, //The FHIR Authorization Server's token endpoint URL - _claims, - _now, - _now.AddMinutes(5) - ); - } - - else - { - //HL7 FHIR IG profile - jwtPayload = new JwtPayLoadExtension( - _clientId, //TODO:: Let user pick the subject alt name. Create will need extra param. - _tokenEndoint, //The FHIR Authorization Server's token endpoint URL - _claims, - _now, - _now.AddMinutes(5) - ); - } + + //HL7 FHIR IG profile + jwtPayload = new JwtPayLoadExtension( + _clientId, //TODO:: Let user pick the subject alt name. Create will need extra param. + _tokenEndoint, //The FHIR Authorization Server's token endpoint URL + _claims, + _now, + _now.AddMinutes(5) + ); if (_extensions != null) { From d7722092f4033ccd4397bae185421a8f3cb6b849 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 12:19:58 +0000 Subject: [PATCH 38/57] Bump Udap.Client, Udap.Common and Udap.Util Bumps [Udap.Client](https://github.com/JoeShook/udap-dotnet), Udap.Common and Udap.Util. These dependencies needed to be updated together. Updates `Udap.Client` from 0.3.40 to 0.3.41 - [Commits](https://github.com/JoeShook/udap-dotnet/compare/v0.3.40...v0.3.41) Updates `Udap.Common` from 0.3.40 to 0.3.41 Updates `Udap.Util` from 0.3.40 to 0.3.41 --- updated-dependencies: - dependency-name: Udap.Client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Common dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Util dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj | 2 +- .../clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj index e692fc87..48152c3b 100644 --- a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj +++ b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + diff --git a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj index 2ec14813..9b949738 100644 --- a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj +++ b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + From 5ec1534b3334e99a531ffdce43bccc7a8e4ae0e8 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Sun, 12 May 2024 07:18:43 -0700 Subject: [PATCH 39/57] Fixup udap and idp scope rules and assertions Enforce and test for Section 6.1 and 6.2 in the HL7 [Security IG](https://hl7.org/fhir/us/udap-security/user.html). --- Udap.Server/Configuration/ServerSettings.cs | 8 +- .../UdapAuthorizationResponseMiddleware.cs | 57 +++- .../TieredOAuthAuthenticationHandler.cs | 8 + .../TieredOAuthAuthenticationOptions.cs | 2 + .../Pages/UdapTieredLogin/Challenge.cshtml | 5 +- .../Pages/UdapTieredLogin/Challenge.cshtml.cs | 32 +- .../Conformance/Tiered/TieredOauthTests.cs | 275 +++++++++++++++++- 7 files changed, 362 insertions(+), 25 deletions(-) diff --git a/Udap.Server/Configuration/ServerSettings.cs b/Udap.Server/Configuration/ServerSettings.cs index 822866e1..b1870381 100644 --- a/Udap.Server/Configuration/ServerSettings.cs +++ b/Udap.Server/Configuration/ServerSettings.cs @@ -34,7 +34,13 @@ public class ServerSettings /// [JsonPropertyName("ForceStateParamOnAuthorizationCode")] public bool ForceStateParamOnAuthorizationCode { get; set; } = false; - + + /// + /// Indicate if the IdentityServer can act as a UDAP enabled IdP. + /// + [JsonPropertyName("TieredIdp")] + public bool TieredIdp { get; set; } = false; + [JsonPropertyName("LogoRequired")] public bool LogoRequired { get; set; } = true; diff --git a/Udap.Server/Hosting/UdapAuthorizationResponseMiddleware.cs b/Udap.Server/Hosting/UdapAuthorizationResponseMiddleware.cs index 102c83a4..c269ed64 100644 --- a/Udap.Server/Hosting/UdapAuthorizationResponseMiddleware.cs +++ b/Udap.Server/Hosting/UdapAuthorizationResponseMiddleware.cs @@ -18,6 +18,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; using Udap.Server.Configuration; +using Udap.Util.Extensions; using static IdentityModel.OidcConstants; namespace Udap.Server.Hosting; @@ -81,7 +82,7 @@ public async Task Invoke( context.Request.Path.Value.Contains(Constants.ProtocolRoutePaths.Authorize)) { var requestParams = context.Request.Query; - + if (requestParams.Any()) { if (udapServerOptions.ForceStateParamOnAuthorizationCode) @@ -90,7 +91,7 @@ public async Task Invoke( { var client = await clients.FindClientByIdAsync( - requestParams.AsNameValueCollection().Get(AuthorizeRequest.ClientId)); + requestParams.AsNameValueCollection().Get(AuthorizeRequest.ClientId) ?? string.Empty); if (client != null && client.ClientSecrets.Any(cs => @@ -98,6 +99,37 @@ await clients.FindClientByIdAsync( { await RenderMissingStateErrorResponse(context); _logger.LogInformation($"{nameof(UdapAuthorizationResponseMiddleware)} executed"); + + return; + } + } + } + + // + // During Tiered OAuth from data holder to IdP the udap and idp scopes are required + // https://hl7.org/fhir/us/udap-security/user.html#data-holder-authentication-request-to-idp + // + if (udapServerOptions.TieredIdp) + { + var requestParamCollection = context.Request.Query.AsNameValueCollection(); + var client = + await clients.FindClientByIdAsync( + requestParamCollection.Get(AuthorizeRequest.ClientId) ?? string.Empty); + var scope = requestParamCollection.Get(AuthorizeRequest.Scope); + + var scopes = scope?.FromSpaceSeparatedString(); + var udap = (scopes ?? new string[] { }).FirstOrDefault(s => s == "udap"); + var openid = (scopes ?? new string[] { }).FirstOrDefault(s => s == "openid"); + + if (client != null && + client.ClientSecrets.Any(cs => + cs.Type == UdapServerConstants.SecretTypes.UDAP_SAN_URI_ISS_NAME)) + { + if (udap.IsNullOrEmpty() || openid.IsNullOrEmpty()) + { + await RenderRequiredScopeErrorResponse(context); + _logger.LogInformation($"{nameof(UdapAuthorizationResponseMiddleware)} executed"); + return; } } @@ -121,7 +153,7 @@ await clients.FindClientByIdAsync( var client = await clients.FindClientByIdAsync( requestParamCollection.Get(AuthorizeRequest.ClientId)); - var scope = requestParamCollection.Get(AuthorizeRequest.Scope); + if (client == null) { @@ -144,6 +176,25 @@ await clients.FindClientByIdAsync( await _next(context); } + private Task RenderRequiredScopeErrorResponse(HttpContext context) + { + if (context.Request.Query.TryGetValue( + AuthorizeRequest.RedirectUri, + out StringValues redirectUri)) + { + var url = BuildRedirectUrl( + context, + redirectUri, + AuthorizeErrors.InvalidRequest, + "Missing udap and/or openid scope between data holder and IdP"); + + context.Response.Redirect(url); + } + + return Task.CompletedTask; + } + + private Task RenderMissingStateErrorResponse(HttpContext context) { if (context.Request.Query.TryGetValue( diff --git a/Udap.Server/Security/Authentication/TieredOAuth/TieredOAuthAuthenticationHandler.cs b/Udap.Server/Security/Authentication/TieredOAuth/TieredOAuthAuthenticationHandler.cs index ad7c8e54..3727f963 100644 --- a/Udap.Server/Security/Authentication/TieredOAuth/TieredOAuthAuthenticationHandler.cs +++ b/Udap.Server/Security/Authentication/TieredOAuth/TieredOAuthAuthenticationHandler.cs @@ -456,6 +456,14 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop var idpParam = (requestParams.GetValues("idp") ?? throw new InvalidOperationException()).Last(); var scope = (requestParams.GetValues("scope") ?? throw new InvalidOperationException()).First(); + + var udap = scope.FromSpaceSeparatedString().FirstOrDefault(s => s == UdapConstants.StandardScopes.Udap); + + if (udap == null) + { + throw new Exception("Missing required udap scope from client for Tiered OAuth"); + } + var clientRedirectUrl = (requestParams.GetValues("redirect_uri") ?? throw new InvalidOperationException()).Last(); var updateRegistration = requestParams.GetValues("update_registration")?.Last(); diff --git a/Udap.Server/Security/Authentication/TieredOAuth/TieredOAuthAuthenticationOptions.cs b/Udap.Server/Security/Authentication/TieredOAuth/TieredOAuthAuthenticationOptions.cs index 1f53d9a2..14ad9554 100644 --- a/Udap.Server/Security/Authentication/TieredOAuth/TieredOAuthAuthenticationOptions.cs +++ b/Udap.Server/Security/Authentication/TieredOAuth/TieredOAuthAuthenticationOptions.cs @@ -11,6 +11,7 @@ using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using IdentityModel; +using Udap.Model; namespace Udap.Server.Security.Authentication.TieredOAuth; @@ -24,6 +25,7 @@ public TieredOAuthAuthenticationOptions() SignInScheme = TieredOAuthAuthenticationDefaults.AuthenticationScheme; // TODO: configurable for the non-dynamic AddTieredOAuthForTests call. + Scope.Add(UdapConstants.StandardScopes.Udap); Scope.Add(OidcConstants.StandardScopes.OpenId); Scope.Add(OidcConstants.StandardScopes.Email); Scope.Add(OidcConstants.StandardScopes.Profile); diff --git a/Udap.UI/Pages/UdapTieredLogin/Challenge.cshtml b/Udap.UI/Pages/UdapTieredLogin/Challenge.cshtml index 885d4393..2e5efc7f 100644 --- a/Udap.UI/Pages/UdapTieredLogin/Challenge.cshtml +++ b/Udap.UI/Pages/UdapTieredLogin/Challenge.cshtml @@ -9,11 +9,12 @@ - + Challenge
- + Failed to process Tiered OAuth request. + Ensure the udap scope and idp parameter are included.
\ No newline at end of file diff --git a/Udap.UI/Pages/UdapTieredLogin/Challenge.cshtml.cs b/Udap.UI/Pages/UdapTieredLogin/Challenge.cshtml.cs index 6b23a2a6..cd26626c 100644 --- a/Udap.UI/Pages/UdapTieredLogin/Challenge.cshtml.cs +++ b/Udap.UI/Pages/UdapTieredLogin/Challenge.cshtml.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; using Udap.Client.Client; using Udap.Server.Security.Authentication.TieredOAuth; @@ -22,25 +23,36 @@ public class Challenge : PageModel { private readonly IIdentityServerInteractionService _interactionService; private readonly IUdapClient _udapClient; + private readonly ILogger _logger; - public Challenge(IIdentityServerInteractionService interactionService, IUdapClient udapClient) + public Challenge(IIdentityServerInteractionService interactionService, IUdapClient udapClient, ILogger logger) { _interactionService = interactionService; _udapClient = udapClient; + _logger = logger; } public async Task OnGetAsync(string scheme, string returnUrl) { if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; - - var props = await TieredOAuthHelpers.BuildDynamicTieredOAuthOptions( - _interactionService, - _udapClient, - scheme, - "/udaptieredlogin/callback", - returnUrl); - // start challenge and roundtrip the return URL and scheme - return Challenge(props, scheme); + try + { + var props = await TieredOAuthHelpers.BuildDynamicTieredOAuthOptions( + _interactionService, + _udapClient, + scheme, + "/udaptieredlogin/callback", + returnUrl); + + // start challenge and roundtrip the return URL and scheme + return Challenge(props, scheme); + } + catch (Exception ex) + { + _logger.LogWarning($"Failed Tiered Oauth for returnUrl: {returnUrl}"); + } + + return Page(); } } \ No newline at end of file diff --git a/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs b/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs index 188a0bc7..ce36fa88 100644 --- a/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs @@ -13,11 +13,14 @@ using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Text; +using System.Text.Encodings.Web; using System.Text.Json; +using System.Web; using Duende.IdentityServer; using Duende.IdentityServer.Models; using Duende.IdentityServer.Test; using FluentAssertions; +using FluentAssertions.Common; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.WebUtilities; @@ -64,13 +67,9 @@ public TieredOauthTests(ITestOutputHelper testOutputHelper) _community2Anchor = new X509Certificate2("CertStore/anchors/caLocalhostCert2.cer"); _community2IntermediateCert = new X509Certificate2("CertStore/intermediates/intermediateLocalhostCert2.cer"); - - BuildUdapAuthorizationServer(); - BuildUdapIdentityProvider1(); - BuildUdapIdentityProvider2(); - } + } - private void BuildUdapAuthorizationServer() + private void BuildUdapAuthorizationServer(List? tieredOAuthScopes = null) { _mockAuthorServerPipeline.OnPostConfigureServices += services => { @@ -96,6 +95,18 @@ private void BuildUdapAuthorizationServer() // Allow logo resolve back to udap.auth server // services.AddSingleton(_ => _mockAuthorServerPipeline.BrowserClient); + + if (tieredOAuthScopes != null) + { + services.ConfigureAll(options => + { + options.Scope.Clear(); + foreach (var tieredOAuthScope in tieredOAuthScopes) + { + options.Scope.Add(tieredOAuthScope); + } + }); + } }; _mockAuthorServerPipeline.OnPreConfigureServices += (builderContext, services) => @@ -240,7 +251,8 @@ private void BuildUdapIdentityProvider1() DefaultSystemScopes = "udap", // ForceStateParamOnAuthorizationCode = false (default) AlwaysIncludeUserClaimsInIdToken = true, - RequireConsent = false + RequireConsent = false, + TieredIdp = true }); // This registers Clients as List so downstream I can pick it up in InMemoryUdapClientRegistrationStore @@ -407,6 +419,10 @@ private void BuildUdapIdentityProvider2() [Fact] public async Task ClientAuthorize_IdPDiscovery_IdPRegistration_IdPAuthAccess_ClientAuthAccess_Test() { + BuildUdapAuthorizationServer(); + BuildUdapIdentityProvider1(); + + // Register client with auth server var resultDocument = await RegisterClientWithAuthServer(); _mockAuthorServerPipeline.RemoveSessionCookie(); @@ -515,8 +531,7 @@ public async Task ClientAuthorize_IdPDiscovery_IdPRegistration_IdPAuthAccess_Cli QueryHelpers.ParseQuery(backChannelChallengeResponse.Headers.Location.Query).Single(p => p.Key == "client_id").Value.Should().NotBeEmpty(); var backChannelState = QueryHelpers.ParseQuery(backChannelChallengeResponse.Headers.Location.Query).Single(p => p.Key == "state").Value.ToString(); backChannelState.Should().NotBeNullOrEmpty(); - - + var idpClient = _mockIdPPipeline.Clients.Single(c => c.ClientName == "AuthServer Client"); idpClient.AlwaysIncludeUserClaimsInIdToken.Should().BeTrue(); @@ -697,6 +712,9 @@ public async Task ClientAuthorize_IdPDiscovery_IdPRegistration_IdPAuthAccess_Cli [Fact] //(Skip = "Dynamic Tiered OAuth Provider WIP")] public async Task Tiered_OAuth_With_DynamicProvider() { + BuildUdapAuthorizationServer(); + BuildUdapIdentityProvider2(); + // Register client with auth server var resultDocument = await RegisterClientWithAuthServer(); _mockAuthorServerPipeline.RemoveSessionCookie(); @@ -978,6 +996,245 @@ public async Task Tiered_OAuth_With_DynamicProvider() } + + /// + /// During Tiered OAuth between the client and data holder the udap scope is required + /// Client call to /authorize? should request with udap scope. + /// Without it the idp is undefined according to https://hl7.org/fhir/us/udap-security/user.html#client-authorization-request-to-data-holder + /// + /// + [Fact] + public async Task ClientAuthorize_Missing_udap_scope_between_client_and_dataholder_Test() + { + BuildUdapAuthorizationServer(); + BuildUdapIdentityProvider1(); + + // Register client with auth server + var resultDocument = await RegisterClientWithAuthServer(); + _mockAuthorServerPipeline.RemoveSessionCookie(); + _mockAuthorServerPipeline.RemoveLoginCookie(); + resultDocument.Should().NotBeNull(); + resultDocument!.ClientId.Should().NotBeNull(); + + var clientId = resultDocument.ClientId!; + + var dynamicIdp = _mockAuthorServerPipeline.ApplicationServices.GetRequiredService(); + dynamicIdp.Name = _mockIdPPipeline.BaseUrl; + + ////////////////////// + // ClientAuthorize + ////////////////////// + + // Data Holder's Auth Server validates Identity Provider's Server software statement + + var clientState = Guid.NewGuid().ToString(); + + var clientAuthorizeUrl = _mockAuthorServerPipeline.CreateAuthorizeUrl( + clientId: clientId, + responseType: "code", + scope: "openid user/*.read", + redirectUri: "https://code_client/callback", + state: clientState, + extra: new + { + idp = "https://idpserver" + }); + + _mockAuthorServerPipeline.BrowserClient.AllowAutoRedirect = false; + // The BrowserHandler.cs will normally set the cookie to indicate user signed in. + // We want to skip that and get a redirect to the login page + _mockAuthorServerPipeline.BrowserClient.AllowCookies = false; + var response = await _mockAuthorServerPipeline.BrowserClient.GetAsync(clientAuthorizeUrl); + response.StatusCode.Should().Be(HttpStatusCode.Redirect, await response.Content.ReadAsStringAsync()); + response.Headers.Location.Should().NotBeNull(); + response.Headers.Location!.AbsoluteUri.Should().Contain("https://server/Account/Login"); + // _testOutputHelper.WriteLine(response.Headers.Location!.AbsoluteUri); + var queryParams = QueryHelpers.ParseQuery(response.Headers.Location.Query); + queryParams.Should().Contain(p => p.Key == "ReturnUrl"); + queryParams.Should().NotContain(p => p.Key == "code"); + queryParams.Should().NotContain(p => p.Key == "state"); + + + // Pull the inner query params from the ReturnUrl + var returnUrl = queryParams.Single(p => p.Key == "ReturnUrl").Value.ToString(); + returnUrl.Should().StartWith("/connect/authorize/callback?"); + queryParams = QueryHelpers.ParseQuery(returnUrl); + queryParams.Single(q => q.Key == "scope").Value.ToString().Should().Contain("openid user/*.read"); + queryParams.Single(q => q.Key == "state").Value.Should().BeEquivalentTo(clientState); + queryParams.Single(q => q.Key == "idp").Value.Should().BeEquivalentTo("https://idpserver"); + + var schemes = await _mockAuthorServerPipeline.Resolve().GetAllSchemesAsync(); + + var sb = new StringBuilder(); + sb.Append("https://server/externallogin/challenge?"); // built in UdapAccount/Login/Index.cshtml.cs + sb.Append("scheme=").Append(schemes.First().Name); + sb.Append("&returnUrl=").Append(Uri.EscapeDataString(returnUrl)); + clientAuthorizeUrl = sb.ToString(); + + + // response after discovery and registration + _mockAuthorServerPipeline.BrowserClient.AllowCookies = true; // Need to set the idsrv cookie so calls to /authorize will succeed + + _mockAuthorServerPipeline.BrowserClient.GetXsrfCookie("https://server/federation/udap-tiered/signin", + new TieredOAuthAuthenticationOptions().CorrelationCookie.Name!).Should().BeNull(); + + var exception = await Assert.ThrowsAsync(() => _mockAuthorServerPipeline.BrowserClient.GetAsync(clientAuthorizeUrl)); + exception.Message.Should().Be("Missing required udap scope from client for Tiered OAuth"); + } + + + /// + /// During Tiered OAuth between data holder and IdP the openid and udap scope are required + /// Client call to /authorize? should request with udap scope. + /// https://hl7.org/fhir/us/udap-security/user.html#data-holder-authentication-request-to-idp + /// + /// + [Theory] + [InlineData(new object[] { new string[] { "openid", "email", "profile"}})] + [InlineData(new object[] { new string[] { "udap", "email", "profile" } })] + public async Task ClientAuthorize_Missing_udap_or_idp_scope_between_dataholder_and_IdP_Test(string[] scopes) + { + // var scopes = new List() { "email", "profile" }; + BuildUdapAuthorizationServer(scopes.ToList()); + BuildUdapIdentityProvider1(); + + // Register client with auth server + var resultDocument = await RegisterClientWithAuthServer(); + _mockAuthorServerPipeline.RemoveSessionCookie(); + _mockAuthorServerPipeline.RemoveLoginCookie(); + resultDocument.Should().NotBeNull(); + resultDocument!.ClientId.Should().NotBeNull(); + + var clientId = resultDocument.ClientId!; + + var dynamicIdp = _mockAuthorServerPipeline.ApplicationServices.GetRequiredService(); + dynamicIdp.Name = _mockIdPPipeline.BaseUrl; + + ////////////////////// + // ClientAuthorize + ////////////////////// + + // Data Holder's Auth Server validates Identity Provider's Server software statement + + var clientState = Guid.NewGuid().ToString(); + + var clientAuthorizeUrl = _mockAuthorServerPipeline.CreateAuthorizeUrl( + clientId: clientId, + responseType: "code", + scope: "udap openid user/*.read", + redirectUri: "https://code_client/callback", + state: clientState, + extra: new + { + idp = "https://idpserver" + }); + + _mockAuthorServerPipeline.BrowserClient.AllowAutoRedirect = false; + // The BrowserHandler.cs will normally set the cookie to indicate user signed in. + // We want to skip that and get a redirect to the login page + _mockAuthorServerPipeline.BrowserClient.AllowCookies = false; + var response = await _mockAuthorServerPipeline.BrowserClient.GetAsync(clientAuthorizeUrl); + response.StatusCode.Should().Be(HttpStatusCode.Redirect, await response.Content.ReadAsStringAsync()); + response.Headers.Location.Should().NotBeNull(); + response.Headers.Location!.AbsoluteUri.Should().Contain("https://server/Account/Login"); + // _testOutputHelper.WriteLine(response.Headers.Location!.AbsoluteUri); + var queryParams = QueryHelpers.ParseQuery(response.Headers.Location.Query); + queryParams.Should().Contain(p => p.Key == "ReturnUrl"); + queryParams.Should().NotContain(p => p.Key == "code"); + queryParams.Should().NotContain(p => p.Key == "state"); + + + // Pull the inner query params from the ReturnUrl + var returnUrl = queryParams.Single(p => p.Key == "ReturnUrl").Value.ToString(); + returnUrl.Should().StartWith("/connect/authorize/callback?"); + queryParams = QueryHelpers.ParseQuery(returnUrl); + queryParams.Single(q => q.Key == "scope").Value.ToString().Should().Contain("udap openid user/*.read"); + queryParams.Single(q => q.Key == "state").Value.Should().BeEquivalentTo(clientState); + queryParams.Single(q => q.Key == "idp").Value.Should().BeEquivalentTo("https://idpserver"); + + var schemes = await _mockAuthorServerPipeline.Resolve().GetAllSchemesAsync(); + + var sb = new StringBuilder(); + sb.Append("https://server/externallogin/challenge?"); // built in UdapAccount/Login/Index.cshtml.cs + sb.Append("scheme=").Append(schemes.First().Name); + sb.Append("&returnUrl=").Append(Uri.EscapeDataString(returnUrl)); + clientAuthorizeUrl = sb.ToString(); + + + + ////////////////////////////////// + // + // IdPDiscovery + // IdPRegistration + // IdPAuthAccess + // + ////////////////////////////////// + + + // Auto Dynamic registration between Auth Server and Identity Provider happens here. + // /Challenge? + // ctx.ChallengeAsync -> launch registered scheme. In this case the TieredOauthAuthenticationHandler + // see: OnExternalLoginChallenge and Challenge(props, scheme) in ExternalLogin/Challenge.cshtml.cs or UdapTieredLogin/Challenge.cshtml.cs + // Backchannel + // Discovery + // Auto registration + // externalloging/challenge or in the Udap implementation it is the UdapAccount/Login/Index.cshtml.cs. XSRF cookie is set here. + + // *** We are here after the request to the IdPs /authorize call. If the client is registered already then Discovery and Reg is skipped *** + // + // Authentication request (/authorize?) + // User logs in at IdP + // Authentication response + // Token request + // Data Holder incorporates user input into authorization decision + // + + + + // response after discovery and registration + _mockAuthorServerPipeline.BrowserClient.AllowCookies = + true; // Need to set the idsrv cookie so calls to /authorize will succeed + + _mockAuthorServerPipeline.BrowserClient.GetXsrfCookie("https://server/federation/udap-tiered/signin", + new TieredOAuthAuthenticationOptions().CorrelationCookie.Name!).Should().BeNull(); + var backChannelChallengeResponse = await _mockAuthorServerPipeline.BrowserClient.GetAsync(clientAuthorizeUrl); + _mockAuthorServerPipeline.BrowserClient.GetXsrfCookie("https://server/federation/udap-tiered/signin", + new TieredOAuthAuthenticationOptions().CorrelationCookie.Name!).Should().NotBeNull(); + + backChannelChallengeResponse.StatusCode.Should().Be(HttpStatusCode.Redirect, + await backChannelChallengeResponse.Content.ReadAsStringAsync()); + backChannelChallengeResponse.Headers.Location.Should().NotBeNull(); + backChannelChallengeResponse.Headers.Location!.AbsoluteUri.Should() + .StartWith("https://idpserver/connect/authorize"); + + // _testOutputHelper.WriteLine(backChannelChallengeResponse.Headers.Location!.AbsoluteUri); + QueryHelpers.ParseQuery(backChannelChallengeResponse.Headers.Location.Query).Single(p => p.Key == "client_id") + .Value.Should().NotBeEmpty(); + var backChannelState = QueryHelpers.ParseQuery(backChannelChallengeResponse.Headers.Location.Query) + .Single(p => p.Key == "state").Value.ToString(); + backChannelState.Should().NotBeNullOrEmpty(); + + var idpClient = _mockIdPPipeline.Clients.Single(c => c.ClientName == "AuthServer Client"); + idpClient.AlwaysIncludeUserClaimsInIdToken.Should().BeTrue(); + + + Debug.Assert(_mockIdPPipeline.BrowserClient != null, "_mockIdPPipeline.BrowserClient != null"); + var backChannelAuthResult = + await _mockIdPPipeline.BrowserClient.GetAsync(backChannelChallengeResponse.Headers.Location); + _testOutputHelper.WriteLine(HttpUtility.UrlDecode(backChannelAuthResult.Headers.Location.Query)); + + backChannelAuthResult.StatusCode.Should().Be(HttpStatusCode.Redirect, + await backChannelAuthResult.Content.ReadAsStringAsync()); + backChannelAuthResult.Headers.Location.Should().NotBeNull(); + backChannelAuthResult.Headers.Location!.AbsoluteUri.Should() + .StartWith("https://server/federation/udap-tiered/signin"); //signin callback scheme + + var responseParams = QueryHelpers.ParseQuery(backChannelAuthResult.Headers.Location.Query); + responseParams["error"].Should().BeEquivalentTo("invalid_request"); + responseParams["error_description"].Should().BeEquivalentTo("Missing udap and/or openid scope between data holder and IdP"); + responseParams["scope"].Should().BeEquivalentTo(scopes.ToSpaceSeparatedString()); + } + private async Task RegisterClientWithAuthServer() { var clientCert = new X509Certificate2("CertStore/issued/fhirLabsApiClientLocalhostCert.pfx", "udap-test"); From 8c11eeb9cfd8f1eda5f0dda478aad72c6e5416ee Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Sun, 12 May 2024 09:01:14 -0700 Subject: [PATCH 40/57] Don't expose TieredIdP as a configurable property Working on the final deprecation of ServerSupport property. Just need to prove that the udap.org/TestTool is in line with Security IG. I may be wrong on this. Have to deploy and test to prove it out. --- .../UdapServerServiceCollectionExtensions.cs | 2 + Udap.Server/Configuration/ServerSettings.cs | 8 +- .../UdapDynamicClientRegistrationValidator.cs | 53 +- .../TieredOAuth/TieredIdpServerSettings.cs | 26 + .../Common/ConnectaThon/HealthGorillaTests.cs | 16 +- .../RegistrationAndChangeRegistrationTests.cs | 16 +- .../Conformance/Basic/ScopeExpansionTests.cs | 19 +- .../Conformance/Tiered/TieredOauthTests.cs | 48 +- .../UdapServer.Tests/UdapRegistrationTests.cs | 2750 ++++++++--------- 9 files changed, 1482 insertions(+), 1456 deletions(-) create mode 100644 Udap.Server/Security/Authentication/TieredOAuth/TieredIdpServerSettings.cs diff --git a/Udap.Server/Configuration/DependencyInjection/UdapServerServiceCollectionExtensions.cs b/Udap.Server/Configuration/DependencyInjection/UdapServerServiceCollectionExtensions.cs index b04bce8c..5716bfce 100644 --- a/Udap.Server/Configuration/DependencyInjection/UdapServerServiceCollectionExtensions.cs +++ b/Udap.Server/Configuration/DependencyInjection/UdapServerServiceCollectionExtensions.cs @@ -28,6 +28,7 @@ using Udap.Server.Mappers; using Udap.Server.Options; using Udap.Server.ResponseHandling; +using Udap.Server.Security.Authentication.TieredOAuth; using Udap.Server.Stores; using Udap.Server.Validation; using static Udap.Server.Constants; @@ -166,6 +167,7 @@ public static IUdapServiceBuilder AddUdapServerAsIdentityProvider( services.Configure(setupAction); } + builder.Services.TryAddSingleton, TieredIdpServerSettings>(); builder.AddUdapSigningCredentials(); services.AddSingleton(resolver => resolver.GetRequiredService>().Value); builder.AddRegistrationEndpointToOpenIdConnectMetadata(baseUrl); diff --git a/Udap.Server/Configuration/ServerSettings.cs b/Udap.Server/Configuration/ServerSettings.cs index b1870381..623d9677 100644 --- a/Udap.Server/Configuration/ServerSettings.cs +++ b/Udap.Server/Configuration/ServerSettings.cs @@ -13,9 +13,9 @@ namespace Udap.Server.Configuration; public class ServerSettings { - [JsonPropertyName("ServerSupport")] - [JsonConverter(typeof(JsonStringEnumConverter))] - public ServerSupport ServerSupport { get; set; } + // [JsonPropertyName("ServerSupport")] + // [JsonConverter(typeof(JsonStringEnumConverter))] + // public ServerSupport ServerSupport { get; set; } [JsonPropertyName("DefaultSystemScopes")] public string? DefaultSystemScopes { get; set; } @@ -38,7 +38,7 @@ public class ServerSettings /// /// Indicate if the IdentityServer can act as a UDAP enabled IdP. /// - [JsonPropertyName("TieredIdp")] + [JsonIgnore] public bool TieredIdp { get; set; } = false; [JsonPropertyName("LogoRequired")] diff --git a/Udap.Server/Registration/UdapDynamicClientRegistrationValidator.cs b/Udap.Server/Registration/UdapDynamicClientRegistrationValidator.cs index fe118aed..92153b6f 100644 --- a/Udap.Server/Registration/UdapDynamicClientRegistrationValidator.cs +++ b/Udap.Server/Registration/UdapDynamicClientRegistrationValidator.cs @@ -89,7 +89,7 @@ IEnumerable anchors { using var activity = Tracing.ValidationActivitySource.StartActivity("UdapDynamicClientRegistrationValidator.Validate"); - _logger.LogDebug($"Start client validation with Server Support Type {_serverSettings.ServerSupport}"); + // _logger.LogDebug($"Start client validation with Server Support Type {_serverSettings.ServerSupport}"); var tokenHandler = new JsonWebTokenHandler(); @@ -452,7 +452,7 @@ IEnumerable anchors ////////////////////////////// if (client.AllowedGrantTypes.Count != 0 && //Cancel Registration - _serverSettings.ServerSupport == ServerSupport.Hl7SecurityIG && + // _serverSettings.ServerSupport == ServerSupport.Hl7SecurityIG && (document.Scope == null || !document.Scope.Any())) { return await Task.FromResult(new UdapDynamicClientRegistrationValidationResult( @@ -474,30 +474,31 @@ IEnumerable anchors // // Also there should be a better way to do this. It will repeat many scope entries per client. // - if (_serverSettings.ServerSupport == ServerSupport.UDAP) - { - if (string.IsNullOrWhiteSpace(document.Scope)) - { - IEnumerable? scopes = null; - - if (document.GrantTypes != null && document.GrantTypes.Contains(OidcConstants.GrantTypes.ClientCredentials)) - { - scopes = _serverSettings.DefaultSystemScopes?.FromSpaceSeparatedString(); - } - else if (document.GrantTypes != null && document.GrantTypes.Contains(OidcConstants.GrantTypes.AuthorizationCode)) - { - scopes = _serverSettings.DefaultUserScopes?.FromSpaceSeparatedString(); - } - - if (scopes != null) - { - foreach (var scope in scopes) - { - client?.AllowedScopes.Add(scope); - } - } - } - } + // TODO: Remove when we prove we no longer need legacy UDAP server support + // if (_serverSettings.ServerSupport == ServerSupport.UDAP) + // { + // if (string.IsNullOrWhiteSpace(document.Scope)) + // { + // IEnumerable? scopes = null; + // + // if (document.GrantTypes != null && document.GrantTypes.Contains(OidcConstants.GrantTypes.ClientCredentials)) + // { + // scopes = _serverSettings.DefaultSystemScopes?.FromSpaceSeparatedString(); + // } + // else if (document.GrantTypes != null && document.GrantTypes.Contains(OidcConstants.GrantTypes.AuthorizationCode)) + // { + // scopes = _serverSettings.DefaultUserScopes?.FromSpaceSeparatedString(); + // } + // + // if (scopes != null) + // { + // foreach (var scope in scopes) + // { + // client?.AllowedScopes.Add(scope); + // } + // } + // } + // } if (document.Scope != null && document.Any()) { var scopes = document.Scope.Split(' ', StringSplitOptions.RemoveEmptyEntries); diff --git a/Udap.Server/Security/Authentication/TieredOAuth/TieredIdpServerSettings.cs b/Udap.Server/Security/Authentication/TieredOAuth/TieredIdpServerSettings.cs new file mode 100644 index 00000000..05c2c843 --- /dev/null +++ b/Udap.Server/Security/Authentication/TieredOAuth/TieredIdpServerSettings.cs @@ -0,0 +1,26 @@ +#region (c) 2024 Joseph Shook. All rights reserved. +// /* +// Authors: +// Joseph Shook Joseph.Shook@Surescripts.com +// +// See LICENSE in the project root for license information. +// */ +#endregion + +using Microsoft.Extensions.Options; +using Udap.Server.Configuration; + +namespace Udap.Server.Security.Authentication.TieredOAuth; + +public class TieredIdpServerSettings : IPostConfigureOptions +{ + /// + /// Invoked to configure a instance. + /// + /// The name of the options instance being configured. + /// The options instance to configured. + public void PostConfigure(string? name, ServerSettings options) + { + options.TieredIdp = true; + } +} \ No newline at end of file diff --git a/_tests/UdapServer.Tests/Common/ConnectaThon/HealthGorillaTests.cs b/_tests/UdapServer.Tests/Common/ConnectaThon/HealthGorillaTests.cs index 5d0c8030..69398bbd 100644 --- a/_tests/UdapServer.Tests/Common/ConnectaThon/HealthGorillaTests.cs +++ b/_tests/UdapServer.Tests/Common/ConnectaThon/HealthGorillaTests.cs @@ -8,8 +8,10 @@ using Duende.IdentityServer.Models; using Duende.IdentityServer.Test; using FluentAssertions; +using FluentAssertions.Common; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Udap.Client.Configuration; using Udap.Common.Models; using Udap.Model; @@ -37,17 +39,11 @@ public HealthGorillaTests(ITestOutputHelper testOutputHelper) var sureFhirLabsAnchor = new X509Certificate2("CertStore/anchors/SureFhirLabs_CA.cer"); var intermediateCert = new X509Certificate2("CertStore/intermediates/SureFhirLabs_Intermediate.cer"); - _mockPipeline.OnPostConfigureServices += s => + _mockPipeline.OnPostConfigureServices += services => { - s.AddSingleton(new ServerSettings - { - ServerSupport = ServerSupport.UDAP, - DefaultUserScopes = "udap", - DefaultSystemScopes = "udap", - ForceStateParamOnAuthorizationCode = true - }); - - s.AddSingleton(new UdapClientOptions + services.AddSingleton(sp => sp.GetRequiredService>().Value); + + services.AddSingleton(new UdapClientOptions { ClientName = "Mock Client", Contacts = new HashSet { "mailto:Joseph.Shook@Surescripts.com", "mailto:JoeShook@gmail.com" } diff --git a/_tests/UdapServer.Tests/Conformance/Basic/RegistrationAndChangeRegistrationTests.cs b/_tests/UdapServer.Tests/Conformance/Basic/RegistrationAndChangeRegistrationTests.cs index 4aa423c8..59cecd9f 100644 --- a/_tests/UdapServer.Tests/Conformance/Basic/RegistrationAndChangeRegistrationTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Basic/RegistrationAndChangeRegistrationTests.cs @@ -14,8 +14,10 @@ using System.Text.Json; using Duende.IdentityServer.Models; using FluentAssertions; +using FluentAssertions.Common; using IdentityModel; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Udap.Client.Configuration; using Udap.Common.Models; using Udap.Model; @@ -45,17 +47,11 @@ public RegistrationAndChangeRegistrationTests(ITestOutputHelper testOutputHelper var anchorCommunity2 = new X509Certificate2("CertStore/anchors/caLocalhostCert2.cer"); var intermediateCommunity2 = new X509Certificate2("CertStore/intermediates/intermediateLocalhostCert2.cer"); - _mockPipeline.OnPostConfigureServices += s => + _mockPipeline.OnPostConfigureServices += services => { - s.AddSingleton(new ServerSettings - { - ServerSupport = ServerSupport.UDAP, - DefaultUserScopes = "udap", - DefaultSystemScopes = "udap", - ForceStateParamOnAuthorizationCode = true - }); - - s.AddSingleton(new UdapClientOptions + services.AddSingleton(sp => sp.GetRequiredService>().Value); + + services.AddSingleton(new UdapClientOptions { ClientName = "Mock Client", Contacts = new HashSet { "mailto:Joseph.Shook@Surescripts.com", "mailto:JoeShook@gmail.com" } diff --git a/_tests/UdapServer.Tests/Conformance/Basic/ScopeExpansionTests.cs b/_tests/UdapServer.Tests/Conformance/Basic/ScopeExpansionTests.cs index 04c19359..8a444c91 100644 --- a/_tests/UdapServer.Tests/Conformance/Basic/ScopeExpansionTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Basic/ScopeExpansionTests.cs @@ -16,10 +16,12 @@ using Duende.IdentityServer.Models; using Duende.IdentityServer.Test; using FluentAssertions; +using FluentAssertions.Common; using IdentityModel; using IdentityModel.Client; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Udap.Client.Client.Extensions; using Udap.Client.Configuration; @@ -52,23 +54,22 @@ public ScopeExpansionTests(ITestOutputHelper testOutputHelper) var sureFhirLabsAnchor = new X509Certificate2("CertStore/anchors/SureFhirLabs_CA.cer"); var intermediateCert = new X509Certificate2("CertStore/intermediates/SureFhirLabs_Intermediate.cer"); - _mockPipeline.OnPostConfigureServices += s => + _mockPipeline.OnPostConfigureServices += services => { - s.AddSingleton(new ServerSettings + services.AddSingleton(sp => { - ServerSupport = ServerSupport.UDAP, - DefaultUserScopes = "udap", - DefaultSystemScopes = "udap", - RequireConsent = false + var serverSettings = sp.GetRequiredService>().Value; + serverSettings.RequireConsent = false; + return serverSettings; }); - s.AddSingleton(new UdapClientOptions + services.AddSingleton(new UdapClientOptions { ClientName = "Mock Client", Contacts = new HashSet { "mailto:Joseph.Shook@Surescripts.com", "mailto:JoeShook@gmail.com" } }); - - s.AddScoped(); + + services.AddScoped(); }; _mockPipeline.OnPreConfigureServices += (_, s) => diff --git a/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs b/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs index ce36fa88..2069c98b 100644 --- a/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs @@ -75,9 +75,6 @@ private void BuildUdapAuthorizationServer(List? tieredOAuthScopes = null { services.AddSingleton(new ServerSettings { - ServerSupport = ServerSupport.Hl7SecurityIG, - // DefaultUserScopes = "udap", - // DefaultSystemScopes = "udap" ForceStateParamOnAuthorizationCode = true, //false (default) RequireConsent = false }); @@ -244,16 +241,19 @@ private void BuildUdapIdentityProvider1() { _mockIdPPipeline.OnPostConfigureServices += services => { - services.AddSingleton(new ServerSettings - { - ServerSupport = ServerSupport.UDAP, - DefaultUserScopes = "udap", - DefaultSystemScopes = "udap", - // ForceStateParamOnAuthorizationCode = false (default) - AlwaysIncludeUserClaimsInIdToken = true, - RequireConsent = false, - TieredIdp = true - }); + services.AddSingleton( + sp => + { + var serverSettings = sp.GetService>().Value; // must resolve to trigger the post config at TieredIdpServerSettings + serverSettings.DefaultUserScopes = "udap"; + serverSettings.DefaultSystemScopes = "udap"; + // ForceStateParamOnAuthorizationCode = false (default) + serverSettings.AlwaysIncludeUserClaimsInIdToken = true; + serverSettings.RequireConsent = false; + + return serverSettings; + }); + // This registers Clients as List so downstream I can pick it up in InMemoryUdapClientRegistrationStore // Duende's AddInMemoryClients extension registers as IEnumerable and is used in InMemoryClientStore as readonly. @@ -330,15 +330,19 @@ private void BuildUdapIdentityProvider2() { _mockIdPPipeline2.OnPostConfigureServices += services => { - services.AddSingleton(new ServerSettings - { - ServerSupport = ServerSupport.UDAP, - DefaultUserScopes = "udap", - DefaultSystemScopes = "udap", - // ForceStateParamOnAuthorizationCode = false (default) - AlwaysIncludeUserClaimsInIdToken = true, - RequireConsent = false - }); + services.AddSingleton( + sp => + { + var serverSettings = sp.GetService>().Value; + serverSettings.DefaultUserScopes = "udap"; + serverSettings.DefaultSystemScopes = "udap"; + // ForceStateParamOnAuthorizationCode = false (default) + serverSettings.AlwaysIncludeUserClaimsInIdToken = true; + serverSettings.RequireConsent = false; + + return serverSettings; + }); + // This registers Clients as List so downstream I can pick it up in InMemoryUdapClientRegistrationStore // Duende's AddInMemoryClients extension registers as IEnumerable and is used in InMemoryClientStore as readonly. diff --git a/_tests/UdapServer.Tests/UdapRegistrationTests.cs b/_tests/UdapServer.Tests/UdapRegistrationTests.cs index 1dc4bef9..e4eed714 100644 --- a/_tests/UdapServer.Tests/UdapRegistrationTests.cs +++ b/_tests/UdapServer.Tests/UdapRegistrationTests.cs @@ -1,1375 +1,1375 @@ -#region (c) 2023 Joseph Shook. All rights reserved. -// /* -// Authors: -// Joseph Shook Joseph.Shook@Surescripts.com -// -// See LICENSE in the project root for license information. -// */ -#endregion - -using System.Net; -using System.Net.Http.Json; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Text.Json; -using Duende.IdentityServer.EntityFramework.DbContexts; -using FluentAssertions; -using Hl7.Fhir.Model; -using IdentityModel; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.IdentityModel.Tokens; -using NSubstitute; -using Udap.Client.Client.Extensions; -using Udap.Common.Certificates; -using Udap.Model; -using Udap.Model.Registration; -using Udap.Model.Statement; -using Udap.Server.DbContexts; -using Xunit.Abstractions; -using Task = System.Threading.Tasks.Task; - -namespace UdapServer.Tests; - -public class UdapApiTestFixture : WebApplicationFactory -{ - public ITestOutputHelper? Output { get; set; } - public IUdapDbAdminContext UdapDbAdminContext { get; set; } = null!; - - private ServiceProvider _serviceProvider = null!; - private IServiceScope _serviceScope = null!; - - - public UdapApiTestFixture() - { - SeedData.EnsureSeedData("Data Source=./Udap.Idp.db;", Substitute.For()).GetAwaiter().GetResult(); - } - - protected override IHost CreateHost(IHostBuilder builder) - { - Environment.SetEnvironmentVariable("ASPNETCORE_URLS", "http://localhost"); - //Similar to pushing to the cloud where the docker image runs as localhost:8080 but we want to inform Udap.Idp - //that it is some other https url for settings like aud, register and other metadata published settings. - Environment.SetEnvironmentVariable("UdapIdpBaseUrl", "http://localhost"); - Environment.SetEnvironmentVariable("provider", "Sqlite"); - builder.UseEnvironment("Development"); - - builder.ConfigureServices(services => - { - services.AddSingleton(); - - // - // Fix-up TrustChainValidator to ignore certificate revocation - // - var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(TrustChainValidator)); - - - if (descriptor != null) - { - Console.WriteLine($"Removing {descriptor}"); - services.Remove(descriptor); - } - else - { - Console.WriteLine("Nothing to remove???"); - } - - services.AddSingleton(new TrustChainValidator( - new X509ChainPolicy - { - VerificationFlags = X509VerificationFlags.IgnoreWrongUsage, - RevocationFlag = X509RevocationFlag.ExcludeRoot, - RevocationMode = X509RevocationMode.NoCheck // This is the change unit testing with no revocation endpoint to host the revocation list. - }, - Output!.ToLogger())); - - _serviceProvider = services.BuildServiceProvider(); - _serviceScope = _serviceProvider.GetRequiredService().CreateScope(); - UdapDbAdminContext = _serviceScope.ServiceProvider.GetRequiredService(); - }); - - var overrideSettings = new Dictionary - { - { "ConnectionStrings:DefaultConnection", "Data Source=Udap.Idp.db;" }, - { "ServerSettings:ServerSupport", "UDAP"}, - { "ServerSettings:LogoRequired", "false"} - - }; - - var sb = new StringBuilder(); - - foreach (var resName in ModelInfo.SupportedResources) - { - sb.Append(' ').Append($"user/{resName}.*"); - sb.Append(' ').Append($"user/{resName}.read"); - } - - overrideSettings.Add("ServerSettings:DefaultUserScopes", sb.ToString().TrimStart()); - - sb = new StringBuilder(); - - foreach (var resName in ModelInfo.SupportedResources) - { - sb.Append(' ').Append($"system/{resName}.*"); - sb.Append(' ').Append($"system/{resName}.read"); - } - - overrideSettings.Add("ServerSettings:DefaultSystemScopes", sb.ToString().TrimStart()); - - - - builder.ConfigureHostConfiguration(b => b.AddInMemoryCollection(overrideSettings!)); - - builder.ConfigureLogging(logging => - { - logging.ClearProviders(); - logging.AddXUnit(Output!); - }); - - var app = base.CreateHost(builder); - - return app; - } - - /// - public override async ValueTask DisposeAsync() - { - _serviceScope.Dispose(); - await _serviceProvider.DisposeAsync(); - } - - protected override void ConfigureWebHost(IWebHostBuilder builder) - { - builder.UseSetting("skipRateLimiting", null); - - // - // Linux needs to know how to find appsettings file in web api under test. - // Still works with Windows but what a pain. This feels fragile - // TODO: - // - //This is not working for linux tests like it did in other projects. - builder.UseSetting("contentRoot", "../../../../../examples/Udap.Auth.Server/"); - } -} - -/// -/// Full Web tests. Using web server. -/// -[Collection("Udap.Auth.Server")] -public class UdapServerRegistrationTests : IClassFixture -{ - private UdapApiTestFixture _fixture; - private readonly ITestOutputHelper _testOutputHelper; - - public UdapServerRegistrationTests(UdapApiTestFixture fixture, ITestOutputHelper testOutputHelper) - { - if (fixture == null) throw new ArgumentNullException(nameof(fixture)); - fixture.Output = testOutputHelper; - _fixture = fixture; - _testOutputHelper = testOutputHelper; - } - - [Fact] - public async Task RegistrationSuccess_authorization_code_Test() - { - using var client = _fixture.CreateClient(); - await ResetClientInDatabase(); - - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - // var discoJsonFormatted = - // JsonSerializer.Serialize(disco.Json, new JsonSerializerOptions { WriteIndented = true }); - // _testOutputHelper.WriteLine(discoJsonFormatted); - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine("CertStore/issued", - "weatherApiClientLocalhostCert1.pfx"); - - _testOutputHelper.WriteLine($"Path to Cert: {cert}"); - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - // - // Could use JwtPayload. But because we have a typed object, UdapDynamicClientRegistrationDocument - // I have it implementing IDictionary so the JsonExtensions.SerializeToJson method - // can prepare it the same way JwtPayLoad is essentially implemented, but light weight - // and specific to this Udap Dynamic Registration. - // - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "http://localhost/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "authorization_code", "refresh_token" }, - ResponseTypes = new HashSet { "code" }, - RedirectUris = new List(){ "http://localhost/signin-oidc" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "user/Patient.*" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = - await client.PostAsJsonAsync(reg, - requestBody); //TODO on server side fail for Certifications empty collection - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.Created); - - // var documentAsJson = JsonSerializer.Serialize(document); - // var result = await response.Content.ReadAsStringAsync(); - // _testOutputHelper.WriteLine(result); - // result.Should().BeEquivalentTo(documentAsJson); - - var responseUdapDocument = - await response.Content.ReadFromJsonAsync(); - - responseUdapDocument.Should().NotBeNull(); - responseUdapDocument!.ClientId.Should().NotBeNullOrEmpty(); - _testOutputHelper.WriteLine(JsonSerializer.Serialize(responseUdapDocument, - new JsonSerializerOptions { WriteIndented = true })); - - // - // Assertions according to - // https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1 - // - responseUdapDocument.SoftwareStatement.Should().Be(signedSoftwareStatement); - responseUdapDocument.ClientName.Should().Be(document.ClientName); - responseUdapDocument.Issuer.Should().Be(document.Issuer); - - ((JsonElement)responseUdapDocument["Extra"]).GetString().Should().Be(document["Extra"].ToString()); - - - using var scope = _fixture.Services.GetRequiredService().CreateScope(); - var udapContext = scope.ServiceProvider.GetRequiredService(); - - var clientEntity = await udapContext.Clients - .Include(c => c.RedirectUris) - .SingleAsync(c => c.ClientId == responseUdapDocument.ClientId); - - clientEntity.RequirePkce.Should().BeFalse(); - - clientEntity.RedirectUris.Single().RedirectUri.Should().Be("http://localhost/signin-oidc"); - clientEntity.AllowOfflineAccess.Should().BeTrue(); - } - - [Fact] - public async Task RegistrationSuccessTest() - { - using var client = _fixture.CreateClient(); - await ResetClientInDatabase(); - - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - // var discoJsonFormatted = - // JsonSerializer.Serialize(disco.Json, new JsonSerializerOptions { WriteIndented = true }); - // _testOutputHelper.WriteLine(discoJsonFormatted); - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine("CertStore/issued", "weatherApiClientLocalhostCert1.pfx"); - - _testOutputHelper.WriteLine($"Path to Cert: {cert}"); - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = - await client.PostAsJsonAsync(reg, - requestBody); //TODO on server side fail for Certifications empty collection - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.Created); - - // var documentAsJson = JsonSerializer.Serialize(document); - // var result = await response.Content.ReadAsStringAsync(); - // _testOutputHelper.WriteLine(result); - // result.Should().BeEquivalentTo(documentAsJson); - - var responseUdapDocument = - await response.Content.ReadFromJsonAsync(); - - responseUdapDocument.Should().NotBeNull(); - responseUdapDocument!.ClientId.Should().NotBeNullOrEmpty(); - _testOutputHelper.WriteLine(JsonSerializer.Serialize(responseUdapDocument, - new JsonSerializerOptions { WriteIndented = true })); - - // - // Assertions according to - // https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1 - // - responseUdapDocument.SoftwareStatement.Should().Be(signedSoftwareStatement); - responseUdapDocument.ClientName.Should().Be(document.ClientName); - responseUdapDocument.Issuer.Should().Be(document.Issuer); - - ((JsonElement)responseUdapDocument["Extra"]).GetString().Should().Be(document["Extra"].ToString()); - - - using var scope = _fixture.Services.GetRequiredService().CreateScope(); - var udapContext = scope.ServiceProvider.GetRequiredService(); - - var clientEntity = await udapContext.Clients - .Include(c => c.AllowedScopes) - .SingleAsync(c => c.ClientId == responseUdapDocument.ClientId); - - clientEntity.RequirePkce.Should().BeTrue(); - - clientEntity.AllowedScopes.Count.Should().Be(ModelInfo.SupportedResources.Count * 2); - clientEntity.AllowOfflineAccess.Should().BeFalse(); - } - - [Fact] - public async Task RegistrationMissingx5cHeaderTest() - { - // var clientPolicyStore = _fixture.Services.GetService(); - // - // - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - // var discoJsonFormatted = - // JsonSerializer.Serialize(disco.Json, new JsonSerializerOptions { WriteIndented = true }); - // _testOutputHelper.WriteLine(discoJsonFormatted); - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "https://weatherapi.lab:5021/fhir", - Subject = "https://weatherapi.lab:5021/fhir", - Audience = "https://weatherapi.lab:5021/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - } - - //invalid_software_statement - [Fact] - public async Task RegistrationInvalidSoftwareStatement_Signature_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost:5002/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement + "Invalid", - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - } - - //invalid_software_statement - [Fact] - public async Task RegisrationInvalidSotwareStatement_issMatchesUriName_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost:9999/", - Subject = "http://localhost/", - Audience = "https://localhost:5002/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - } - - //invalid_software_statement - [Fact] - public async Task RegisrationInvalidSotwareStatement_issMissing_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - // Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost:5002/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - } - - //invalid_software_statement - [Fact] - public async Task RegisrationInvalidSotwareStatement_subMissing_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - // Subject = "http://localhost/", - Audience = "https://localhost:5002/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - errorResponse.ErrorDescription.Should().Be(UdapDynamicClientRegistrationErrorDescriptions.SubIsMissing); - } - - - //invalid_software_statement - [Fact] - public async Task RegisrationInvalidSotwareStatement_subNotEqualtoIss_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost:9999/", - Audience = "https://localhost:5002/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - errorResponse.ErrorDescription.Should().Be(UdapDynamicClientRegistrationErrorDescriptions.SubNotEqualToIss); - } - - //invalid_software_statement - [Fact] - public async Task RegisrationInvalidSotwareStatement_audMissing_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - // Audience = "https://localhost:5002/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.InvalidAud}: "); - } - - //invalid_software_statement - [Fact] - public async Task RegisrationInvalidSotwareStatement_audEqualsRegistrationEndpoint_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost:5002/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.InvalidMatchAud}"); - } - - //invalid_software_statement - [Fact] - public async Task RegisrationInvalidSotwareStatement_expMissing_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost:5002/connect/register", - // Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.ExpMissing}"); - } - - //invalid_software_statement - [Fact] - public async Task RegisrationInvalidSotwareStatement_expExpired_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost:5002/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(-5).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - errorResponse.ErrorDescription.Should().Contain($"{UdapDynamicClientRegistrationErrorDescriptions.ExpExpired}"); - } - - //invalid_software_statement - [Fact] - public async Task RegisrationInvalidSotwareStatement_iatMissing_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - //IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); - errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.IssuedAtMissing}"); - } - - //invalid_client_metadata - [Fact] - public async Task RegistrationInvalidClientMetadata_clientName_Missing_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - // ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); - errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.ClientNameMissing}"); - } - - //invalid_client_metadata - // - // Remember and empty grant_types is a cancel registration - // http://hl7.org/fhir/us/udap-security/registration.html#modifying-and-cancelling-registrations - // But a missing grant_types is an error - // - [Fact] - public async Task RegisrationInvalidClientMetadata_grant_types_Missing_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - // GrantTypes = new HashSet { "client_credentials" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); - errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.GrantTypeMissing}"); - } - - //invalid_client_metadata - [Fact] - public async Task RegisrationInvalidClientMetadata_responseTypes_Missing_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "authorization_code" }, - TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "user/Patient.* user/Practitioner.read", - RedirectUris = new List { new Uri($"https://client.fhirlabs.net/redirect/{Guid.NewGuid()}").AbsoluteUri }, - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); - errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.ResponseTypesMissing}"); - } - - //invalid_client_metadata - [Fact] - public async Task RegisrationInvalidClientMetadata_tokenEndpointAuthMethodMissing_Test() - { - using var client = _fixture.CreateClient(); - var disco = await client.GetUdapDiscoveryDocument(); - - disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); - disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); - - var regEndpoint = disco.RegistrationEndpoint; - var reg = new Uri(regEndpoint!); - - var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), - "weatherApiClientLocalhostCert1.pfx"); - - var clientCert = new X509Certificate2(cert, "udap-test"); - var now = DateTime.UtcNow; - var jwtId = CryptoRandom.CreateUniqueId(); - - var document = new UdapDynamicClientRegistrationDocument - { - Issuer = "http://localhost/", - Subject = "http://localhost/", - Audience = "https://localhost/connect/register", - Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), - IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), - JwtId = jwtId, - ClientName = "udapTestClient", - Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, - GrantTypes = new HashSet { "client_credentials" }, - //TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, - Scope = "system/Patient.* system/Practitioner.read" - }; - - document.Add("Extra", "Stuff" as string); - - var signedSoftwareStatement = - SignedSoftwareStatementBuilder - .Create(clientCert, document) - .Build(); - - var requestBody = new UdapRegisterRequest - ( - signedSoftwareStatement, - UdapConstants.UdapVersionsSupportedValue - ); - - var response = await client.PostAsJsonAsync(reg, requestBody); - - if (response.StatusCode != HttpStatusCode.Created) - { - _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); - } - - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - - var errorResponse = - await response.Content.ReadFromJsonAsync(); - - errorResponse.Should().NotBeNull(); - errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); - errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.TokenEndpointAuthMethodMissing}"); - } - - private async Task ResetClientInDatabase() - { - foreach (var dbClient in _fixture.UdapDbAdminContext.Clients) - { - _fixture.UdapDbAdminContext.Clients.Remove(dbClient); - } - - await _fixture.UdapDbAdminContext.SaveChangesAsync(); - } -} \ No newline at end of file +// #region (c) 2023 Joseph Shook. All rights reserved. +// // /* +// // Authors: +// // Joseph Shook Joseph.Shook@Surescripts.com +// // +// // See LICENSE in the project root for license information. +// // */ +// #endregion +// +// using System.Net; +// using System.Net.Http.Json; +// using System.Security.Cryptography.X509Certificates; +// using System.Text; +// using System.Text.Json; +// using Duende.IdentityServer.EntityFramework.DbContexts; +// using FluentAssertions; +// using Hl7.Fhir.Model; +// using IdentityModel; +// using Microsoft.AspNetCore.Hosting; +// using Microsoft.AspNetCore.Mvc.Testing; +// using Microsoft.EntityFrameworkCore; +// using Microsoft.Extensions.Configuration; +// using Microsoft.Extensions.DependencyInjection; +// using Microsoft.Extensions.Hosting; +// using Microsoft.Extensions.Logging; +// using Microsoft.IdentityModel.Tokens; +// using NSubstitute; +// using Udap.Client.Client.Extensions; +// using Udap.Common.Certificates; +// using Udap.Model; +// using Udap.Model.Registration; +// using Udap.Model.Statement; +// using Udap.Server.DbContexts; +// using Xunit.Abstractions; +// using Task = System.Threading.Tasks.Task; +// +// namespace UdapServer.Tests; +// +// public class UdapApiTestFixture : WebApplicationFactory +// { +// public ITestOutputHelper? Output { get; set; } +// public IUdapDbAdminContext UdapDbAdminContext { get; set; } = null!; +// +// private ServiceProvider _serviceProvider = null!; +// private IServiceScope _serviceScope = null!; +// +// +// public UdapApiTestFixture() +// { +// SeedData.EnsureSeedData("Data Source=./Udap.Idp.db;", Substitute.For()).GetAwaiter().GetResult(); +// } +// +// protected override IHost CreateHost(IHostBuilder builder) +// { +// Environment.SetEnvironmentVariable("ASPNETCORE_URLS", "http://localhost"); +// //Similar to pushing to the cloud where the docker image runs as localhost:8080 but we want to inform Udap.Idp +// //that it is some other https url for settings like aud, register and other metadata published settings. +// Environment.SetEnvironmentVariable("UdapIdpBaseUrl", "http://localhost"); +// Environment.SetEnvironmentVariable("provider", "Sqlite"); +// builder.UseEnvironment("Development"); +// +// builder.ConfigureServices(services => +// { +// services.AddSingleton(); +// +// // +// // Fix-up TrustChainValidator to ignore certificate revocation +// // +// var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(TrustChainValidator)); +// +// +// if (descriptor != null) +// { +// Console.WriteLine($"Removing {descriptor}"); +// services.Remove(descriptor); +// } +// else +// { +// Console.WriteLine("Nothing to remove???"); +// } +// +// services.AddSingleton(new TrustChainValidator( +// new X509ChainPolicy +// { +// VerificationFlags = X509VerificationFlags.IgnoreWrongUsage, +// RevocationFlag = X509RevocationFlag.ExcludeRoot, +// RevocationMode = X509RevocationMode.NoCheck // This is the change unit testing with no revocation endpoint to host the revocation list. +// }, +// Output!.ToLogger())); +// +// _serviceProvider = services.BuildServiceProvider(); +// _serviceScope = _serviceProvider.GetRequiredService().CreateScope(); +// UdapDbAdminContext = _serviceScope.ServiceProvider.GetRequiredService(); +// }); +// +// var overrideSettings = new Dictionary +// { +// { "ConnectionStrings:DefaultConnection", "Data Source=Udap.Idp.db;" }, +// { "ServerSettings:ServerSupport", "UDAP"}, +// { "ServerSettings:LogoRequired", "false"} +// +// }; +// +// var sb = new StringBuilder(); +// +// foreach (var resName in ModelInfo.SupportedResources) +// { +// sb.Append(' ').Append($"user/{resName}.*"); +// sb.Append(' ').Append($"user/{resName}.read"); +// } +// +// overrideSettings.Add("ServerSettings:DefaultUserScopes", sb.ToString().TrimStart()); +// +// sb = new StringBuilder(); +// +// foreach (var resName in ModelInfo.SupportedResources) +// { +// sb.Append(' ').Append($"system/{resName}.*"); +// sb.Append(' ').Append($"system/{resName}.read"); +// } +// +// overrideSettings.Add("ServerSettings:DefaultSystemScopes", sb.ToString().TrimStart()); +// +// +// +// builder.ConfigureHostConfiguration(b => b.AddInMemoryCollection(overrideSettings!)); +// +// builder.ConfigureLogging(logging => +// { +// logging.ClearProviders(); +// logging.AddXUnit(Output!); +// }); +// +// var app = base.CreateHost(builder); +// +// return app; +// } +// +// /// +// public override async ValueTask DisposeAsync() +// { +// _serviceScope.Dispose(); +// await _serviceProvider.DisposeAsync(); +// } +// +// protected override void ConfigureWebHost(IWebHostBuilder builder) +// { +// builder.UseSetting("skipRateLimiting", null); +// +// // +// // Linux needs to know how to find appsettings file in web api under test. +// // Still works with Windows but what a pain. This feels fragile +// // TODO: +// // +// //This is not working for linux tests like it did in other projects. +// builder.UseSetting("contentRoot", "../../../../../examples/Udap.Auth.Server/"); +// } +// } +// +// /// +// /// Full Web tests. Using web server. +// /// +// [Collection("Udap.Auth.Server")] +// public class UdapServerRegistrationTests : IClassFixture +// { +// private UdapApiTestFixture _fixture; +// private readonly ITestOutputHelper _testOutputHelper; +// +// public UdapServerRegistrationTests(UdapApiTestFixture fixture, ITestOutputHelper testOutputHelper) +// { +// if (fixture == null) throw new ArgumentNullException(nameof(fixture)); +// fixture.Output = testOutputHelper; +// _fixture = fixture; +// _testOutputHelper = testOutputHelper; +// } +// +// [Fact] +// public async Task RegistrationSuccess_authorization_code_Test() +// { +// using var client = _fixture.CreateClient(); +// await ResetClientInDatabase(); +// +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// // var discoJsonFormatted = +// // JsonSerializer.Serialize(disco.Json, new JsonSerializerOptions { WriteIndented = true }); +// // _testOutputHelper.WriteLine(discoJsonFormatted); +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine("CertStore/issued", +// "weatherApiClientLocalhostCert1.pfx"); +// +// _testOutputHelper.WriteLine($"Path to Cert: {cert}"); +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// // +// // Could use JwtPayload. But because we have a typed object, UdapDynamicClientRegistrationDocument +// // I have it implementing IDictionary so the JsonExtensions.SerializeToJson method +// // can prepare it the same way JwtPayLoad is essentially implemented, but light weight +// // and specific to this Udap Dynamic Registration. +// // +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "http://localhost/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "authorization_code", "refresh_token" }, +// ResponseTypes = new HashSet { "code" }, +// RedirectUris = new List(){ "http://localhost/signin-oidc" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "user/Patient.*" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = +// await client.PostAsJsonAsync(reg, +// requestBody); //TODO on server side fail for Certifications empty collection +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.Created); +// +// // var documentAsJson = JsonSerializer.Serialize(document); +// // var result = await response.Content.ReadAsStringAsync(); +// // _testOutputHelper.WriteLine(result); +// // result.Should().BeEquivalentTo(documentAsJson); +// +// var responseUdapDocument = +// await response.Content.ReadFromJsonAsync(); +// +// responseUdapDocument.Should().NotBeNull(); +// responseUdapDocument!.ClientId.Should().NotBeNullOrEmpty(); +// _testOutputHelper.WriteLine(JsonSerializer.Serialize(responseUdapDocument, +// new JsonSerializerOptions { WriteIndented = true })); +// +// // +// // Assertions according to +// // https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1 +// // +// responseUdapDocument.SoftwareStatement.Should().Be(signedSoftwareStatement); +// responseUdapDocument.ClientName.Should().Be(document.ClientName); +// responseUdapDocument.Issuer.Should().Be(document.Issuer); +// +// ((JsonElement)responseUdapDocument["Extra"]).GetString().Should().Be(document["Extra"].ToString()); +// +// +// using var scope = _fixture.Services.GetRequiredService().CreateScope(); +// var udapContext = scope.ServiceProvider.GetRequiredService(); +// +// var clientEntity = await udapContext.Clients +// .Include(c => c.RedirectUris) +// .SingleAsync(c => c.ClientId == responseUdapDocument.ClientId); +// +// clientEntity.RequirePkce.Should().BeFalse(); +// +// clientEntity.RedirectUris.Single().RedirectUri.Should().Be("http://localhost/signin-oidc"); +// clientEntity.AllowOfflineAccess.Should().BeTrue(); +// } +// +// [Fact] +// public async Task RegistrationSuccessTest() +// { +// using var client = _fixture.CreateClient(); +// await ResetClientInDatabase(); +// +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// // var discoJsonFormatted = +// // JsonSerializer.Serialize(disco.Json, new JsonSerializerOptions { WriteIndented = true }); +// // _testOutputHelper.WriteLine(discoJsonFormatted); +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine("CertStore/issued", "weatherApiClientLocalhostCert1.pfx"); +// +// _testOutputHelper.WriteLine($"Path to Cert: {cert}"); +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = +// await client.PostAsJsonAsync(reg, +// requestBody); //TODO on server side fail for Certifications empty collection +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.Created); +// +// // var documentAsJson = JsonSerializer.Serialize(document); +// // var result = await response.Content.ReadAsStringAsync(); +// // _testOutputHelper.WriteLine(result); +// // result.Should().BeEquivalentTo(documentAsJson); +// +// var responseUdapDocument = +// await response.Content.ReadFromJsonAsync(); +// +// responseUdapDocument.Should().NotBeNull(); +// responseUdapDocument!.ClientId.Should().NotBeNullOrEmpty(); +// _testOutputHelper.WriteLine(JsonSerializer.Serialize(responseUdapDocument, +// new JsonSerializerOptions { WriteIndented = true })); +// +// // +// // Assertions according to +// // https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1 +// // +// responseUdapDocument.SoftwareStatement.Should().Be(signedSoftwareStatement); +// responseUdapDocument.ClientName.Should().Be(document.ClientName); +// responseUdapDocument.Issuer.Should().Be(document.Issuer); +// +// ((JsonElement)responseUdapDocument["Extra"]).GetString().Should().Be(document["Extra"].ToString()); +// +// +// using var scope = _fixture.Services.GetRequiredService().CreateScope(); +// var udapContext = scope.ServiceProvider.GetRequiredService(); +// +// var clientEntity = await udapContext.Clients +// .Include(c => c.AllowedScopes) +// .SingleAsync(c => c.ClientId == responseUdapDocument.ClientId); +// +// clientEntity.RequirePkce.Should().BeTrue(); +// +// clientEntity.AllowedScopes.Count.Should().Be(ModelInfo.SupportedResources.Count * 2); +// clientEntity.AllowOfflineAccess.Should().BeFalse(); +// } +// +// [Fact] +// public async Task RegistrationMissingx5cHeaderTest() +// { +// // var clientPolicyStore = _fixture.Services.GetService(); +// // +// // +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// // var discoJsonFormatted = +// // JsonSerializer.Serialize(disco.Json, new JsonSerializerOptions { WriteIndented = true }); +// // _testOutputHelper.WriteLine(discoJsonFormatted); +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "https://weatherapi.lab:5021/fhir", +// Subject = "https://weatherapi.lab:5021/fhir", +// Audience = "https://weatherapi.lab:5021/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// } +// +// //invalid_software_statement +// [Fact] +// public async Task RegistrationInvalidSoftwareStatement_Signature_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost:5002/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement + "Invalid", +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// } +// +// //invalid_software_statement +// [Fact] +// public async Task RegisrationInvalidSotwareStatement_issMatchesUriName_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost:9999/", +// Subject = "http://localhost/", +// Audience = "https://localhost:5002/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// } +// +// //invalid_software_statement +// [Fact] +// public async Task RegisrationInvalidSotwareStatement_issMissing_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// // Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost:5002/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// } +// +// //invalid_software_statement +// [Fact] +// public async Task RegisrationInvalidSotwareStatement_subMissing_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// // Subject = "http://localhost/", +// Audience = "https://localhost:5002/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// errorResponse.ErrorDescription.Should().Be(UdapDynamicClientRegistrationErrorDescriptions.SubIsMissing); +// } +// +// +// //invalid_software_statement +// [Fact] +// public async Task RegisrationInvalidSotwareStatement_subNotEqualtoIss_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost:9999/", +// Audience = "https://localhost:5002/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// errorResponse.ErrorDescription.Should().Be(UdapDynamicClientRegistrationErrorDescriptions.SubNotEqualToIss); +// } +// +// //invalid_software_statement +// [Fact] +// public async Task RegisrationInvalidSotwareStatement_audMissing_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// // Audience = "https://localhost:5002/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.InvalidAud}: "); +// } +// +// //invalid_software_statement +// [Fact] +// public async Task RegisrationInvalidSotwareStatement_audEqualsRegistrationEndpoint_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost:5002/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.InvalidMatchAud}"); +// } +// +// //invalid_software_statement +// [Fact] +// public async Task RegisrationInvalidSotwareStatement_expMissing_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost:5002/connect/register", +// // Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.ExpMissing}"); +// } +// +// //invalid_software_statement +// [Fact] +// public async Task RegisrationInvalidSotwareStatement_expExpired_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost:5002/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(-5).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// errorResponse.ErrorDescription.Should().Contain($"{UdapDynamicClientRegistrationErrorDescriptions.ExpExpired}"); +// } +// +// //invalid_software_statement +// [Fact] +// public async Task RegisrationInvalidSotwareStatement_iatMissing_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// //IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); +// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.IssuedAtMissing}"); +// } +// +// //invalid_client_metadata +// [Fact] +// public async Task RegistrationInvalidClientMetadata_clientName_Missing_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// // ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); +// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.ClientNameMissing}"); +// } +// +// //invalid_client_metadata +// // +// // Remember and empty grant_types is a cancel registration +// // http://hl7.org/fhir/us/udap-security/registration.html#modifying-and-cancelling-registrations +// // But a missing grant_types is an error +// // +// [Fact] +// public async Task RegisrationInvalidClientMetadata_grant_types_Missing_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// // GrantTypes = new HashSet { "client_credentials" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); +// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.GrantTypeMissing}"); +// } +// +// //invalid_client_metadata +// [Fact] +// public async Task RegisrationInvalidClientMetadata_responseTypes_Missing_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "authorization_code" }, +// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "user/Patient.* user/Practitioner.read", +// RedirectUris = new List { new Uri($"https://client.fhirlabs.net/redirect/{Guid.NewGuid()}").AbsoluteUri }, +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); +// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.ResponseTypesMissing}"); +// } +// +// //invalid_client_metadata +// [Fact] +// public async Task RegisrationInvalidClientMetadata_tokenEndpointAuthMethodMissing_Test() +// { +// using var client = _fixture.CreateClient(); +// var disco = await client.GetUdapDiscoveryDocument(); +// +// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); +// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); +// +// var regEndpoint = disco.RegistrationEndpoint; +// var reg = new Uri(regEndpoint!); +// +// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), +// "weatherApiClientLocalhostCert1.pfx"); +// +// var clientCert = new X509Certificate2(cert, "udap-test"); +// var now = DateTime.UtcNow; +// var jwtId = CryptoRandom.CreateUniqueId(); +// +// var document = new UdapDynamicClientRegistrationDocument +// { +// Issuer = "http://localhost/", +// Subject = "http://localhost/", +// Audience = "https://localhost/connect/register", +// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), +// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), +// JwtId = jwtId, +// ClientName = "udapTestClient", +// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, +// GrantTypes = new HashSet { "client_credentials" }, +// //TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, +// Scope = "system/Patient.* system/Practitioner.read" +// }; +// +// document.Add("Extra", "Stuff" as string); +// +// var signedSoftwareStatement = +// SignedSoftwareStatementBuilder +// .Create(clientCert, document) +// .Build(); +// +// var requestBody = new UdapRegisterRequest +// ( +// signedSoftwareStatement, +// UdapConstants.UdapVersionsSupportedValue +// ); +// +// var response = await client.PostAsJsonAsync(reg, requestBody); +// +// if (response.StatusCode != HttpStatusCode.Created) +// { +// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); +// } +// +// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); +// +// var errorResponse = +// await response.Content.ReadFromJsonAsync(); +// +// errorResponse.Should().NotBeNull(); +// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); +// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.TokenEndpointAuthMethodMissing}"); +// } +// +// private async Task ResetClientInDatabase() +// { +// foreach (var dbClient in _fixture.UdapDbAdminContext.Clients) +// { +// _fixture.UdapDbAdminContext.Clients.Remove(dbClient); +// } +// +// await _fixture.UdapDbAdminContext.SaveChangesAsync(); +// } +// } \ No newline at end of file From ad548ea3884bee711247bef5c742b03520bad962 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Sun, 12 May 2024 09:08:26 -0700 Subject: [PATCH 41/57] Fixup --- examples/Udap.Auth.Server/HostingExtensions.cs | 2 +- examples/Udap.Identity.Provider/HostingExtensions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Udap.Auth.Server/HostingExtensions.cs b/examples/Udap.Auth.Server/HostingExtensions.cs index 08108087..25e52b91 100644 --- a/examples/Udap.Auth.Server/HostingExtensions.cs +++ b/examples/Udap.Auth.Server/HostingExtensions.cs @@ -71,7 +71,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde var udapServerOptions = builder.Configuration.GetOption("ServerSettings"); options.DefaultSystemScopes = udapServerOptions.DefaultSystemScopes; options.DefaultUserScopes = udapServerOptions.DefaultUserScopes; - options.ServerSupport = udapServerOptions.ServerSupport; + // options.ServerSupport = udapServerOptions.ServerSupport; options.ForceStateParamOnAuthorizationCode = udapServerOptions.ForceStateParamOnAuthorizationCode; options.LogoRequired = udapServerOptions.LogoRequired; options.RequireConsent = udapServerOptions.RequireConsent; diff --git a/examples/Udap.Identity.Provider/HostingExtensions.cs b/examples/Udap.Identity.Provider/HostingExtensions.cs index 3581f21c..ba6d08ab 100644 --- a/examples/Udap.Identity.Provider/HostingExtensions.cs +++ b/examples/Udap.Identity.Provider/HostingExtensions.cs @@ -51,7 +51,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde var udapServerOptions = builder.Configuration.GetOption("ServerSettings"); options.DefaultSystemScopes = udapServerOptions.DefaultSystemScopes; options.DefaultUserScopes = udapServerOptions.DefaultUserScopes; - options.ServerSupport = udapServerOptions.ServerSupport; + // options.ServerSupport = udapServerOptions.ServerSupport; options.ForceStateParamOnAuthorizationCode = udapServerOptions.ForceStateParamOnAuthorizationCode; options.LogoRequired = udapServerOptions.LogoRequired; options.AlwaysIncludeUserClaimsInIdToken = udapServerOptions.AlwaysIncludeUserClaimsInIdToken; From 5439eb493ea0df38005e61bbb96555751c035420 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Sun, 12 May 2024 09:46:46 -0700 Subject: [PATCH 42/57] Final removal of deprecated ServerSupport ServerSetting property. --- Udap.Server/Configuration/ServerSettings.cs | 10 - .../UdapDynamicClientRegistrationValidator.cs | 45 +- Udap.Server/docs/README.md | 1 - .../IdServerRegistrationTests.cs | 11 - .../UdapServer.Tests/Hl7RegistrationTests.cs | 3 +- .../UdapServer.Tests/UdapRegistrationTests.cs | 1375 ----------------- .../Udap.Auth.Server/HostingExtensions.cs | 1 - .../Properties/launchSettings.json | 2 - .../appsettings.Development.json | 1 - .../appsettings.Production.json | 1 - .../appsettings.Development.json | 1 - .../appsettings.Production.json | 1 - .../HostingExtensions.cs | 1 - .../appsettings.Development.json | 1 - .../appsettings.Production.json | 1 - 15 files changed, 2 insertions(+), 1453 deletions(-) delete mode 100644 _tests/UdapServer.Tests/UdapRegistrationTests.cs diff --git a/Udap.Server/Configuration/ServerSettings.cs b/Udap.Server/Configuration/ServerSettings.cs index 623d9677..933ffaef 100644 --- a/Udap.Server/Configuration/ServerSettings.cs +++ b/Udap.Server/Configuration/ServerSettings.cs @@ -13,10 +13,6 @@ namespace Udap.Server.Configuration; public class ServerSettings { - // [JsonPropertyName("ServerSupport")] - // [JsonConverter(typeof(JsonStringEnumConverter))] - // public ServerSupport ServerSupport { get; set; } - [JsonPropertyName("DefaultSystemScopes")] public string? DefaultSystemScopes { get; set; } @@ -56,12 +52,6 @@ public class ServerSettings } -public enum ServerSupport -{ - UDAP = 0, - Hl7SecurityIG = 1 -} - public static class ConfigurationExtension { public static TOptions GetOption(this IConfiguration configuration, string settingKey) diff --git a/Udap.Server/Registration/UdapDynamicClientRegistrationValidator.cs b/Udap.Server/Registration/UdapDynamicClientRegistrationValidator.cs index 92153b6f..3df8586d 100644 --- a/Udap.Server/Registration/UdapDynamicClientRegistrationValidator.cs +++ b/Udap.Server/Registration/UdapDynamicClientRegistrationValidator.cs @@ -88,10 +88,7 @@ IEnumerable anchors ) { using var activity = Tracing.ValidationActivitySource.StartActivity("UdapDynamicClientRegistrationValidator.Validate"); - - // _logger.LogDebug($"Start client validation with Server Support Type {_serverSettings.ServerSupport}"); - - + var tokenHandler = new JsonWebTokenHandler(); var jsonWebToken = tokenHandler.ReadJsonWebToken(request.SoftwareStatement); var jwtHeader = JwtHeader.Base64UrlDeserialize(jsonWebToken.EncodedHeader); @@ -452,53 +449,13 @@ IEnumerable anchors ////////////////////////////// if (client.AllowedGrantTypes.Count != 0 && //Cancel Registration - // _serverSettings.ServerSupport == ServerSupport.Hl7SecurityIG && (document.Scope == null || !document.Scope.Any())) { return await Task.FromResult(new UdapDynamicClientRegistrationValidationResult( UdapDynamicClientRegistrationErrors.InvalidClientMetadata, "scope is required")); } - - // Enrich Scopes: Todo: inject a ScopeEnricher - // TODO: Need a policy engine for various things. UDAP ServerMode allows and empty scope during registration. - // So some kind of policy linked to maybe issued certificate certification and/or community or something - // There are a lot of choices left up to a community. The HL7 ServerMode requires scopes to be sent during registration. - // This doesn't mean the problem is easier it just means we could filter down during registration even if policy - // allowed for a broader list of scopes. - // Below I use ServerSettings from appsettings. This basically says that server is either UDAP or HL7 mode. Well - // sort of. The code is only trying to pass udap.org tests and survive a HL7 connect-a-thon. By putting the logic in - // a policy engine we can have one server UDAP and Hl7 Mode or whatever the policy engine allows. - - // - // Also there should be a better way to do this. It will repeat many scope entries per client. - // - // TODO: Remove when we prove we no longer need legacy UDAP server support - // if (_serverSettings.ServerSupport == ServerSupport.UDAP) - // { - // if (string.IsNullOrWhiteSpace(document.Scope)) - // { - // IEnumerable? scopes = null; - // - // if (document.GrantTypes != null && document.GrantTypes.Contains(OidcConstants.GrantTypes.ClientCredentials)) - // { - // scopes = _serverSettings.DefaultSystemScopes?.FromSpaceSeparatedString(); - // } - // else if (document.GrantTypes != null && document.GrantTypes.Contains(OidcConstants.GrantTypes.AuthorizationCode)) - // { - // scopes = _serverSettings.DefaultUserScopes?.FromSpaceSeparatedString(); - // } - // - // if (scopes != null) - // { - // foreach (var scope in scopes) - // { - // client?.AllowedScopes.Add(scope); - // } - // } - // } - // } if (document.Scope != null && document.Any()) { var scopes = document.Scope.Split(' ', StringSplitOptions.RemoveEmptyEntries); diff --git a/Udap.Server/docs/README.md b/Udap.Server/docs/README.md index db75c561..29a49163 100644 --- a/Udap.Server/docs/README.md +++ b/Udap.Server/docs/README.md @@ -39,7 +39,6 @@ builder.Services.AddIdentityServer() var udapServerOptions = builder.Configuration.GetOption("ServerSettings"); options.DefaultSystemScopes = udapServerOptions.DefaultSystemScopes; options.DefaultUserScopes = udapServerOptions.DefaultUserScopes; - options.ServerSupport = udapServerOptions.ServerSupport; options.ForceStateParamOnAuthorizationCode = udapServerOptions. ForceStateParamOnAuthorizationCode; }, diff --git a/_tests/Udap.Client.System.Tests/IdServerRegistrationTests.cs b/_tests/Udap.Client.System.Tests/IdServerRegistrationTests.cs index c5a465d5..21e12f25 100644 --- a/_tests/Udap.Client.System.Tests/IdServerRegistrationTests.cs +++ b/_tests/Udap.Client.System.Tests/IdServerRegistrationTests.cs @@ -1336,17 +1336,6 @@ public async Task RegistrationSuccess_authorization_code_FhirLabs_desktop_Test() } - - // - // IDP Server must be running in ServerSupport mode of ServerSupport.UDAP for this to fail and pass the test. - // See part of test where getting Access Token - // var jwtPayload = new JwtPayload( - // result.Issuer, - // - // vs normal - // - // var jwtPayload = new JwtPayload( - // result.ClientId, // // If you want Udap.Idp to run in UDAP mode the use "ASPNETCORE_ENVIRONMENT": "Production" to launch. Or // however you get the serer to pickup appsettings.Production.json diff --git a/_tests/UdapServer.Tests/Hl7RegistrationTests.cs b/_tests/UdapServer.Tests/Hl7RegistrationTests.cs index c4f5c2f9..3cea2628 100644 --- a/_tests/UdapServer.Tests/Hl7RegistrationTests.cs +++ b/_tests/UdapServer.Tests/Hl7RegistrationTests.cs @@ -93,8 +93,7 @@ protected override IHost CreateHost(IHostBuilder builder) var overrideSettings = new Dictionary { - { "ConnectionStrings:DefaultConnection", "Data Source=Udap.Idp.db.HL7;" }, - { "ServerSettings:ServerSupport", "Hl7SecurityIG" } + { "ConnectionStrings:DefaultConnection", "Data Source=Udap.Idp.db.HL7;" } }; builder.ConfigureHostConfiguration(b => b.AddInMemoryCollection(overrideSettings!)); diff --git a/_tests/UdapServer.Tests/UdapRegistrationTests.cs b/_tests/UdapServer.Tests/UdapRegistrationTests.cs deleted file mode 100644 index e4eed714..00000000 --- a/_tests/UdapServer.Tests/UdapRegistrationTests.cs +++ /dev/null @@ -1,1375 +0,0 @@ -// #region (c) 2023 Joseph Shook. All rights reserved. -// // /* -// // Authors: -// // Joseph Shook Joseph.Shook@Surescripts.com -// // -// // See LICENSE in the project root for license information. -// // */ -// #endregion -// -// using System.Net; -// using System.Net.Http.Json; -// using System.Security.Cryptography.X509Certificates; -// using System.Text; -// using System.Text.Json; -// using Duende.IdentityServer.EntityFramework.DbContexts; -// using FluentAssertions; -// using Hl7.Fhir.Model; -// using IdentityModel; -// using Microsoft.AspNetCore.Hosting; -// using Microsoft.AspNetCore.Mvc.Testing; -// using Microsoft.EntityFrameworkCore; -// using Microsoft.Extensions.Configuration; -// using Microsoft.Extensions.DependencyInjection; -// using Microsoft.Extensions.Hosting; -// using Microsoft.Extensions.Logging; -// using Microsoft.IdentityModel.Tokens; -// using NSubstitute; -// using Udap.Client.Client.Extensions; -// using Udap.Common.Certificates; -// using Udap.Model; -// using Udap.Model.Registration; -// using Udap.Model.Statement; -// using Udap.Server.DbContexts; -// using Xunit.Abstractions; -// using Task = System.Threading.Tasks.Task; -// -// namespace UdapServer.Tests; -// -// public class UdapApiTestFixture : WebApplicationFactory -// { -// public ITestOutputHelper? Output { get; set; } -// public IUdapDbAdminContext UdapDbAdminContext { get; set; } = null!; -// -// private ServiceProvider _serviceProvider = null!; -// private IServiceScope _serviceScope = null!; -// -// -// public UdapApiTestFixture() -// { -// SeedData.EnsureSeedData("Data Source=./Udap.Idp.db;", Substitute.For()).GetAwaiter().GetResult(); -// } -// -// protected override IHost CreateHost(IHostBuilder builder) -// { -// Environment.SetEnvironmentVariable("ASPNETCORE_URLS", "http://localhost"); -// //Similar to pushing to the cloud where the docker image runs as localhost:8080 but we want to inform Udap.Idp -// //that it is some other https url for settings like aud, register and other metadata published settings. -// Environment.SetEnvironmentVariable("UdapIdpBaseUrl", "http://localhost"); -// Environment.SetEnvironmentVariable("provider", "Sqlite"); -// builder.UseEnvironment("Development"); -// -// builder.ConfigureServices(services => -// { -// services.AddSingleton(); -// -// // -// // Fix-up TrustChainValidator to ignore certificate revocation -// // -// var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(TrustChainValidator)); -// -// -// if (descriptor != null) -// { -// Console.WriteLine($"Removing {descriptor}"); -// services.Remove(descriptor); -// } -// else -// { -// Console.WriteLine("Nothing to remove???"); -// } -// -// services.AddSingleton(new TrustChainValidator( -// new X509ChainPolicy -// { -// VerificationFlags = X509VerificationFlags.IgnoreWrongUsage, -// RevocationFlag = X509RevocationFlag.ExcludeRoot, -// RevocationMode = X509RevocationMode.NoCheck // This is the change unit testing with no revocation endpoint to host the revocation list. -// }, -// Output!.ToLogger())); -// -// _serviceProvider = services.BuildServiceProvider(); -// _serviceScope = _serviceProvider.GetRequiredService().CreateScope(); -// UdapDbAdminContext = _serviceScope.ServiceProvider.GetRequiredService(); -// }); -// -// var overrideSettings = new Dictionary -// { -// { "ConnectionStrings:DefaultConnection", "Data Source=Udap.Idp.db;" }, -// { "ServerSettings:ServerSupport", "UDAP"}, -// { "ServerSettings:LogoRequired", "false"} -// -// }; -// -// var sb = new StringBuilder(); -// -// foreach (var resName in ModelInfo.SupportedResources) -// { -// sb.Append(' ').Append($"user/{resName}.*"); -// sb.Append(' ').Append($"user/{resName}.read"); -// } -// -// overrideSettings.Add("ServerSettings:DefaultUserScopes", sb.ToString().TrimStart()); -// -// sb = new StringBuilder(); -// -// foreach (var resName in ModelInfo.SupportedResources) -// { -// sb.Append(' ').Append($"system/{resName}.*"); -// sb.Append(' ').Append($"system/{resName}.read"); -// } -// -// overrideSettings.Add("ServerSettings:DefaultSystemScopes", sb.ToString().TrimStart()); -// -// -// -// builder.ConfigureHostConfiguration(b => b.AddInMemoryCollection(overrideSettings!)); -// -// builder.ConfigureLogging(logging => -// { -// logging.ClearProviders(); -// logging.AddXUnit(Output!); -// }); -// -// var app = base.CreateHost(builder); -// -// return app; -// } -// -// /// -// public override async ValueTask DisposeAsync() -// { -// _serviceScope.Dispose(); -// await _serviceProvider.DisposeAsync(); -// } -// -// protected override void ConfigureWebHost(IWebHostBuilder builder) -// { -// builder.UseSetting("skipRateLimiting", null); -// -// // -// // Linux needs to know how to find appsettings file in web api under test. -// // Still works with Windows but what a pain. This feels fragile -// // TODO: -// // -// //This is not working for linux tests like it did in other projects. -// builder.UseSetting("contentRoot", "../../../../../examples/Udap.Auth.Server/"); -// } -// } -// -// /// -// /// Full Web tests. Using web server. -// /// -// [Collection("Udap.Auth.Server")] -// public class UdapServerRegistrationTests : IClassFixture -// { -// private UdapApiTestFixture _fixture; -// private readonly ITestOutputHelper _testOutputHelper; -// -// public UdapServerRegistrationTests(UdapApiTestFixture fixture, ITestOutputHelper testOutputHelper) -// { -// if (fixture == null) throw new ArgumentNullException(nameof(fixture)); -// fixture.Output = testOutputHelper; -// _fixture = fixture; -// _testOutputHelper = testOutputHelper; -// } -// -// [Fact] -// public async Task RegistrationSuccess_authorization_code_Test() -// { -// using var client = _fixture.CreateClient(); -// await ResetClientInDatabase(); -// -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// // var discoJsonFormatted = -// // JsonSerializer.Serialize(disco.Json, new JsonSerializerOptions { WriteIndented = true }); -// // _testOutputHelper.WriteLine(discoJsonFormatted); -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine("CertStore/issued", -// "weatherApiClientLocalhostCert1.pfx"); -// -// _testOutputHelper.WriteLine($"Path to Cert: {cert}"); -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// // -// // Could use JwtPayload. But because we have a typed object, UdapDynamicClientRegistrationDocument -// // I have it implementing IDictionary so the JsonExtensions.SerializeToJson method -// // can prepare it the same way JwtPayLoad is essentially implemented, but light weight -// // and specific to this Udap Dynamic Registration. -// // -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "http://localhost/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "authorization_code", "refresh_token" }, -// ResponseTypes = new HashSet { "code" }, -// RedirectUris = new List(){ "http://localhost/signin-oidc" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "user/Patient.*" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = -// await client.PostAsJsonAsync(reg, -// requestBody); //TODO on server side fail for Certifications empty collection -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.Created); -// -// // var documentAsJson = JsonSerializer.Serialize(document); -// // var result = await response.Content.ReadAsStringAsync(); -// // _testOutputHelper.WriteLine(result); -// // result.Should().BeEquivalentTo(documentAsJson); -// -// var responseUdapDocument = -// await response.Content.ReadFromJsonAsync(); -// -// responseUdapDocument.Should().NotBeNull(); -// responseUdapDocument!.ClientId.Should().NotBeNullOrEmpty(); -// _testOutputHelper.WriteLine(JsonSerializer.Serialize(responseUdapDocument, -// new JsonSerializerOptions { WriteIndented = true })); -// -// // -// // Assertions according to -// // https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1 -// // -// responseUdapDocument.SoftwareStatement.Should().Be(signedSoftwareStatement); -// responseUdapDocument.ClientName.Should().Be(document.ClientName); -// responseUdapDocument.Issuer.Should().Be(document.Issuer); -// -// ((JsonElement)responseUdapDocument["Extra"]).GetString().Should().Be(document["Extra"].ToString()); -// -// -// using var scope = _fixture.Services.GetRequiredService().CreateScope(); -// var udapContext = scope.ServiceProvider.GetRequiredService(); -// -// var clientEntity = await udapContext.Clients -// .Include(c => c.RedirectUris) -// .SingleAsync(c => c.ClientId == responseUdapDocument.ClientId); -// -// clientEntity.RequirePkce.Should().BeFalse(); -// -// clientEntity.RedirectUris.Single().RedirectUri.Should().Be("http://localhost/signin-oidc"); -// clientEntity.AllowOfflineAccess.Should().BeTrue(); -// } -// -// [Fact] -// public async Task RegistrationSuccessTest() -// { -// using var client = _fixture.CreateClient(); -// await ResetClientInDatabase(); -// -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// // var discoJsonFormatted = -// // JsonSerializer.Serialize(disco.Json, new JsonSerializerOptions { WriteIndented = true }); -// // _testOutputHelper.WriteLine(discoJsonFormatted); -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine("CertStore/issued", "weatherApiClientLocalhostCert1.pfx"); -// -// _testOutputHelper.WriteLine($"Path to Cert: {cert}"); -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = -// await client.PostAsJsonAsync(reg, -// requestBody); //TODO on server side fail for Certifications empty collection -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.Created); -// -// // var documentAsJson = JsonSerializer.Serialize(document); -// // var result = await response.Content.ReadAsStringAsync(); -// // _testOutputHelper.WriteLine(result); -// // result.Should().BeEquivalentTo(documentAsJson); -// -// var responseUdapDocument = -// await response.Content.ReadFromJsonAsync(); -// -// responseUdapDocument.Should().NotBeNull(); -// responseUdapDocument!.ClientId.Should().NotBeNullOrEmpty(); -// _testOutputHelper.WriteLine(JsonSerializer.Serialize(responseUdapDocument, -// new JsonSerializerOptions { WriteIndented = true })); -// -// // -// // Assertions according to -// // https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.1 -// // -// responseUdapDocument.SoftwareStatement.Should().Be(signedSoftwareStatement); -// responseUdapDocument.ClientName.Should().Be(document.ClientName); -// responseUdapDocument.Issuer.Should().Be(document.Issuer); -// -// ((JsonElement)responseUdapDocument["Extra"]).GetString().Should().Be(document["Extra"].ToString()); -// -// -// using var scope = _fixture.Services.GetRequiredService().CreateScope(); -// var udapContext = scope.ServiceProvider.GetRequiredService(); -// -// var clientEntity = await udapContext.Clients -// .Include(c => c.AllowedScopes) -// .SingleAsync(c => c.ClientId == responseUdapDocument.ClientId); -// -// clientEntity.RequirePkce.Should().BeTrue(); -// -// clientEntity.AllowedScopes.Count.Should().Be(ModelInfo.SupportedResources.Count * 2); -// clientEntity.AllowOfflineAccess.Should().BeFalse(); -// } -// -// [Fact] -// public async Task RegistrationMissingx5cHeaderTest() -// { -// // var clientPolicyStore = _fixture.Services.GetService(); -// // -// // -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// // var discoJsonFormatted = -// // JsonSerializer.Serialize(disco.Json, new JsonSerializerOptions { WriteIndented = true }); -// // _testOutputHelper.WriteLine(discoJsonFormatted); -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "https://weatherapi.lab:5021/fhir", -// Subject = "https://weatherapi.lab:5021/fhir", -// Audience = "https://weatherapi.lab:5021/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// } -// -// //invalid_software_statement -// [Fact] -// public async Task RegistrationInvalidSoftwareStatement_Signature_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost:5002/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement + "Invalid", -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// } -// -// //invalid_software_statement -// [Fact] -// public async Task RegisrationInvalidSotwareStatement_issMatchesUriName_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost:9999/", -// Subject = "http://localhost/", -// Audience = "https://localhost:5002/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// } -// -// //invalid_software_statement -// [Fact] -// public async Task RegisrationInvalidSotwareStatement_issMissing_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// // Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost:5002/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// } -// -// //invalid_software_statement -// [Fact] -// public async Task RegisrationInvalidSotwareStatement_subMissing_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// // Subject = "http://localhost/", -// Audience = "https://localhost:5002/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// errorResponse.ErrorDescription.Should().Be(UdapDynamicClientRegistrationErrorDescriptions.SubIsMissing); -// } -// -// -// //invalid_software_statement -// [Fact] -// public async Task RegisrationInvalidSotwareStatement_subNotEqualtoIss_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost:9999/", -// Audience = "https://localhost:5002/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// errorResponse.ErrorDescription.Should().Be(UdapDynamicClientRegistrationErrorDescriptions.SubNotEqualToIss); -// } -// -// //invalid_software_statement -// [Fact] -// public async Task RegisrationInvalidSotwareStatement_audMissing_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// // Audience = "https://localhost:5002/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.InvalidAud}: "); -// } -// -// //invalid_software_statement -// [Fact] -// public async Task RegisrationInvalidSotwareStatement_audEqualsRegistrationEndpoint_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost:5002/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.InvalidMatchAud}"); -// } -// -// //invalid_software_statement -// [Fact] -// public async Task RegisrationInvalidSotwareStatement_expMissing_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost:5002/connect/register", -// // Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.ExpMissing}"); -// } -// -// //invalid_software_statement -// [Fact] -// public async Task RegisrationInvalidSotwareStatement_expExpired_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost:5002/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(-5).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// errorResponse.ErrorDescription.Should().Contain($"{UdapDynamicClientRegistrationErrorDescriptions.ExpExpired}"); -// } -// -// //invalid_software_statement -// [Fact] -// public async Task RegisrationInvalidSotwareStatement_iatMissing_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// //IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidSoftwareStatement); -// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.IssuedAtMissing}"); -// } -// -// //invalid_client_metadata -// [Fact] -// public async Task RegistrationInvalidClientMetadata_clientName_Missing_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// // ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); -// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.ClientNameMissing}"); -// } -// -// //invalid_client_metadata -// // -// // Remember and empty grant_types is a cancel registration -// // http://hl7.org/fhir/us/udap-security/registration.html#modifying-and-cancelling-registrations -// // But a missing grant_types is an error -// // -// [Fact] -// public async Task RegisrationInvalidClientMetadata_grant_types_Missing_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// // GrantTypes = new HashSet { "client_credentials" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); -// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.GrantTypeMissing}"); -// } -// -// //invalid_client_metadata -// [Fact] -// public async Task RegisrationInvalidClientMetadata_responseTypes_Missing_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "authorization_code" }, -// TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "user/Patient.* user/Practitioner.read", -// RedirectUris = new List { new Uri($"https://client.fhirlabs.net/redirect/{Guid.NewGuid()}").AbsoluteUri }, -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); -// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.ResponseTypesMissing}"); -// } -// -// //invalid_client_metadata -// [Fact] -// public async Task RegisrationInvalidClientMetadata_tokenEndpointAuthMethodMissing_Test() -// { -// using var client = _fixture.CreateClient(); -// var disco = await client.GetUdapDiscoveryDocument(); -// -// disco.HttpResponse.StatusCode.Should().Be(HttpStatusCode.OK); -// disco.IsError.Should().BeFalse($"{disco.Error} :: {disco.HttpErrorReason}"); -// -// var regEndpoint = disco.RegistrationEndpoint; -// var reg = new Uri(regEndpoint!); -// -// var cert = Path.Combine(Path.Combine(AppContext.BaseDirectory, "CertStore/issued"), -// "weatherApiClientLocalhostCert1.pfx"); -// -// var clientCert = new X509Certificate2(cert, "udap-test"); -// var now = DateTime.UtcNow; -// var jwtId = CryptoRandom.CreateUniqueId(); -// -// var document = new UdapDynamicClientRegistrationDocument -// { -// Issuer = "http://localhost/", -// Subject = "http://localhost/", -// Audience = "https://localhost/connect/register", -// Expiration = EpochTime.GetIntDate(now.AddMinutes(1).ToUniversalTime()), -// IssuedAt = EpochTime.GetIntDate(now.ToUniversalTime()), -// JwtId = jwtId, -// ClientName = "udapTestClient", -// Contacts = new HashSet { "FhirJoe@BridgeTown.lab", "FhirJoe@test.lab" }, -// GrantTypes = new HashSet { "client_credentials" }, -// //TokenEndpointAuthMethod = UdapConstants.RegistrationDocumentValues.TokenEndpointAuthMethodValue, -// Scope = "system/Patient.* system/Practitioner.read" -// }; -// -// document.Add("Extra", "Stuff" as string); -// -// var signedSoftwareStatement = -// SignedSoftwareStatementBuilder -// .Create(clientCert, document) -// .Build(); -// -// var requestBody = new UdapRegisterRequest -// ( -// signedSoftwareStatement, -// UdapConstants.UdapVersionsSupportedValue -// ); -// -// var response = await client.PostAsJsonAsync(reg, requestBody); -// -// if (response.StatusCode != HttpStatusCode.Created) -// { -// _testOutputHelper.WriteLine(await response.Content.ReadAsStringAsync()); -// } -// -// response.StatusCode.Should().Be(HttpStatusCode.BadRequest); -// -// var errorResponse = -// await response.Content.ReadFromJsonAsync(); -// -// errorResponse.Should().NotBeNull(); -// errorResponse!.Error.Should().Be(UdapDynamicClientRegistrationErrors.InvalidClientMetadata); -// errorResponse.ErrorDescription.Should().Be($"{UdapDynamicClientRegistrationErrorDescriptions.TokenEndpointAuthMethodMissing}"); -// } -// -// private async Task ResetClientInDatabase() -// { -// foreach (var dbClient in _fixture.UdapDbAdminContext.Clients) -// { -// _fixture.UdapDbAdminContext.Clients.Remove(dbClient); -// } -// -// await _fixture.UdapDbAdminContext.SaveChangesAsync(); -// } -// } \ No newline at end of file diff --git a/examples/Udap.Auth.Server/HostingExtensions.cs b/examples/Udap.Auth.Server/HostingExtensions.cs index 25e52b91..bfd05299 100644 --- a/examples/Udap.Auth.Server/HostingExtensions.cs +++ b/examples/Udap.Auth.Server/HostingExtensions.cs @@ -71,7 +71,6 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde var udapServerOptions = builder.Configuration.GetOption("ServerSettings"); options.DefaultSystemScopes = udapServerOptions.DefaultSystemScopes; options.DefaultUserScopes = udapServerOptions.DefaultUserScopes; - // options.ServerSupport = udapServerOptions.ServerSupport; options.ForceStateParamOnAuthorizationCode = udapServerOptions.ForceStateParamOnAuthorizationCode; options.LogoRequired = udapServerOptions.LogoRequired; options.RequireConsent = udapServerOptions.RequireConsent; diff --git a/examples/Udap.Auth.Server/Properties/launchSettings.json b/examples/Udap.Auth.Server/Properties/launchSettings.json index 01984f75..1aa5bc78 100644 --- a/examples/Udap.Auth.Server/Properties/launchSettings.json +++ b/examples/Udap.Auth.Server/Properties/launchSettings.json @@ -17,7 +17,6 @@ "environmentVariables": { "GCPDeploy": "false", "ASPNETCORE_ENVIRONMENT": "Development", - "ServerSettings:ServerSupport": "UDAP", "ServerSettings:DefaultSystemScopes": "udap system.cruds system/*.rs", "ServerSettings:DefaultUserScopes": "udap user.cruds", "ServerSettings:ForceStateParamOnAuthorizationCode": "true", @@ -32,7 +31,6 @@ "environmentVariables": { "GCPDeploy": "true", "ASPNETCORE_ENVIRONMENT": "Development", - "ServerSettings:ServerSupport": "UDAP", "ServerSettings:DefaultSystemScopes": "udap system.cruds", "ServerSettings:DefaultUserScopes": "udap user.cruds", "UdapIdpBaseUrl": "https://host.docker.internal:5002" diff --git a/examples/Udap.Auth.Server/appsettings.Development.json b/examples/Udap.Auth.Server/appsettings.Development.json index 89948884..7e98e96d 100644 --- a/examples/Udap.Auth.Server/appsettings.Development.json +++ b/examples/Udap.Auth.Server/appsettings.Development.json @@ -22,7 +22,6 @@ }, "ServerSettings": { - "ServerSupport": "Hl7SecurityIG", "LogoRequired": "true" }, diff --git a/examples/Udap.Auth.Server/appsettings.Production.json b/examples/Udap.Auth.Server/appsettings.Production.json index b9be8fd1..e04fbe50 100644 --- a/examples/Udap.Auth.Server/appsettings.Production.json +++ b/examples/Udap.Auth.Server/appsettings.Production.json @@ -22,7 +22,6 @@ "ServerSettings": { - "ServerSupport": "UDAP", "LogoRequired": "true", //https://hl7.org/fhir/smart-app-launch/scopes-and-launch-context.html "DefaultSystemScopes": "openid system/*.rs system/*.read", diff --git a/examples/Udap.Identity.Provider.2/appsettings.Development.json b/examples/Udap.Identity.Provider.2/appsettings.Development.json index 7b65143a..eed4d293 100644 --- a/examples/Udap.Identity.Provider.2/appsettings.Development.json +++ b/examples/Udap.Identity.Provider.2/appsettings.Development.json @@ -7,7 +7,6 @@ }, "ServerSettings": { - "ServerSupport": "UDAP", "DefaultUserScopes": "openid udap fhirUser email profile", "ForceStateParamOnAuthorizationCode": true }, diff --git a/examples/Udap.Identity.Provider.2/appsettings.Production.json b/examples/Udap.Identity.Provider.2/appsettings.Production.json index de57cf40..d31ca33c 100644 --- a/examples/Udap.Identity.Provider.2/appsettings.Production.json +++ b/examples/Udap.Identity.Provider.2/appsettings.Production.json @@ -1,6 +1,5 @@ { "ServerSettings": { - "ServerSupport": "UDAP", "DefaultUserScopes": "openid fhirUser email profile", "ForceStateParamOnAuthorizationCode": true }, diff --git a/examples/Udap.Identity.Provider/HostingExtensions.cs b/examples/Udap.Identity.Provider/HostingExtensions.cs index ba6d08ab..261227f7 100644 --- a/examples/Udap.Identity.Provider/HostingExtensions.cs +++ b/examples/Udap.Identity.Provider/HostingExtensions.cs @@ -51,7 +51,6 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde var udapServerOptions = builder.Configuration.GetOption("ServerSettings"); options.DefaultSystemScopes = udapServerOptions.DefaultSystemScopes; options.DefaultUserScopes = udapServerOptions.DefaultUserScopes; - // options.ServerSupport = udapServerOptions.ServerSupport; options.ForceStateParamOnAuthorizationCode = udapServerOptions.ForceStateParamOnAuthorizationCode; options.LogoRequired = udapServerOptions.LogoRequired; options.AlwaysIncludeUserClaimsInIdToken = udapServerOptions.AlwaysIncludeUserClaimsInIdToken; diff --git a/examples/Udap.Identity.Provider/appsettings.Development.json b/examples/Udap.Identity.Provider/appsettings.Development.json index 893b638a..bf880c3a 100644 --- a/examples/Udap.Identity.Provider/appsettings.Development.json +++ b/examples/Udap.Identity.Provider/appsettings.Development.json @@ -11,7 +11,6 @@ }, "ServerSettings": { - "ServerSupport": "UDAP", "DefaultUserScopes": "openid udap fhirUser email profile", "ForceStateParamOnAuthorizationCode": true, "AlwaysIncludeUserClaimsInIdToken": true diff --git a/examples/Udap.Identity.Provider/appsettings.Production.json b/examples/Udap.Identity.Provider/appsettings.Production.json index 71193336..babcf513 100644 --- a/examples/Udap.Identity.Provider/appsettings.Production.json +++ b/examples/Udap.Identity.Provider/appsettings.Production.json @@ -1,6 +1,5 @@ { "ServerSettings": { - "ServerSupport": "UDAP", "LogoRequired": "false", "DefaultUserScopes": "openid fhirUser email profile", "ForceStateParamOnAuthorizationCode": true, From 8b63187bfb2574987e97eb808a98409802ebb4b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 12:44:26 +0000 Subject: [PATCH 43/57] Bump Udap.Client, Udap.Common and Udap.Util Bumps [Udap.Client](https://github.com/JoeShook/udap-dotnet), Udap.Common and Udap.Util. These dependencies needed to be updated together. Updates `Udap.Client` from 0.3.41 to 0.3.45 - [Commits](https://github.com/JoeShook/udap-dotnet/compare/v0.3.41...v0.3.45) Updates `Udap.Common` from 0.3.41 to 0.3.45 Updates `Udap.Util` from 0.3.41 to 0.3.45 --- updated-dependencies: - dependency-name: Udap.Client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Common dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Util dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj | 2 +- .../clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj index 48152c3b..dfae43ac 100644 --- a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj +++ b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - +
diff --git a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj index 9b949738..5f4146bf 100644 --- a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj +++ b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + From 9cdfe26ce740a275459149850c64e897b670a8c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 12:47:33 +0000 Subject: [PATCH 44/57] Bump Npgsql.EntityFrameworkCore.PostgreSQL and Npgsql Bumps [Npgsql.EntityFrameworkCore.PostgreSQL](https://github.com/npgsql/efcore.pg) and [Npgsql](https://github.com/npgsql/npgsql). These dependencies needed to be updated together. Updates `Npgsql.EntityFrameworkCore.PostgreSQL` from 8.0.2 to 8.0.4 - [Release notes](https://github.com/npgsql/efcore.pg/releases) - [Commits](https://github.com/npgsql/efcore.pg/compare/v8.0.2...v8.0.4) Updates `Npgsql` from 8.0.2 to 8.0.3 - [Release notes](https://github.com/npgsql/npgsql/releases) - [Commits](https://github.com/npgsql/npgsql/compare/v8.0.2...v8.0.3) --- updated-dependencies: - dependency-name: Npgsql.EntityFrameworkCore.PostgreSQL dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Npgsql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 4 ++-- examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj | 2 +- examples/Udap.Auth.Server/Udap.Auth.Server.csproj | 2 +- migrations/UdapDb.Postgres/UdapDb.Postgres.csproj | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 759e5d76..e558e551 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -40,8 +40,8 @@ - - + + diff --git a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj index a8cd4981..1218da1b 100644 --- a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj +++ b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj @@ -18,7 +18,7 @@ - + diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index ec645178..723d0b4a 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -25,7 +25,7 @@ - + diff --git a/migrations/UdapDb.Postgres/UdapDb.Postgres.csproj b/migrations/UdapDb.Postgres/UdapDb.Postgres.csproj index 832010cc..aa443c2b 100644 --- a/migrations/UdapDb.Postgres/UdapDb.Postgres.csproj +++ b/migrations/UdapDb.Postgres/UdapDb.Postgres.csproj @@ -20,7 +20,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + From d653f7b3f5157d236154e5ec6cf0319f6150bb71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 20:19:51 +0000 Subject: [PATCH 45/57] Bump Swashbuckle.AspNetCore from 6.5.0 to 6.6.1 Bumps [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) from 6.5.0 to 6.6.1. - [Release notes](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases) - [Commits](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/compare/v6.5.0...v6.6.1) --- updated-dependencies: - dependency-name: Swashbuckle.AspNetCore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj | 2 +- examples/WeatherApi/WeatherApi.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj index a8cd4981..1331e132 100644 --- a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj +++ b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj @@ -22,7 +22,7 @@ - + diff --git a/examples/WeatherApi/WeatherApi.csproj b/examples/WeatherApi/WeatherApi.csproj index 1ce67b90..b10d2ee5 100644 --- a/examples/WeatherApi/WeatherApi.csproj +++ b/examples/WeatherApi/WeatherApi.csproj @@ -10,7 +10,7 @@ - + From d9a152ed687197c8578752e76ee6ba7dc65202fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 20:20:29 +0000 Subject: [PATCH 46/57] Bump dotnet-ef from 8.0.4 to 8.0.5 Bumps [dotnet-ef](https://github.com/dotnet/efcore) from 8.0.4 to 8.0.5. - [Release notes](https://github.com/dotnet/efcore/releases) - [Commits](https://github.com/dotnet/efcore/compare/v8.0.4...v8.0.5) --- updated-dependencies: - dependency-name: dotnet-ef dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 677ed3f7..93ec8eb6 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "8.0.4", + "version": "8.0.5", "commands": [ "dotnet-ef" ] From 12893ab9ff4e729ec99601141bee5e6484cfaa42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 20:29:04 +0000 Subject: [PATCH 47/57] Bump System.IdentityModel.Tokens.Jwt, Microsoft.IdentityModel.JsonWebTokens and Microsoft.IdentityModel.Tokens Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet), [Microsoft.IdentityModel.JsonWebTokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) and [Microsoft.IdentityModel.Tokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet). These dependencies needed to be updated together. Updates `System.IdentityModel.Tokens.Jwt` from 7.5.1 to 7.5.2 - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/7.5.1...7.5.2) Updates `Microsoft.IdentityModel.JsonWebTokens` from 7.5.1 to 7.5.2 - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/7.5.1...7.5.2) Updates `Microsoft.IdentityModel.Tokens` from 7.5.1 to 7.5.2 - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/7.5.1...7.5.2) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Microsoft.IdentityModel.JsonWebTokens dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Microsoft.IdentityModel.Tokens dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 6 +++--- _tests/Directory.Packages.props | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 759e5d76..e57107fd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -16,7 +16,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/_tests/Directory.Packages.props b/_tests/Directory.Packages.props index c5320d65..9242f559 100644 --- a/_tests/Directory.Packages.props +++ b/_tests/Directory.Packages.props @@ -22,7 +22,7 @@ - + @@ -31,6 +31,6 @@ - + \ No newline at end of file From 70d11b373488068d61bd157fcc55f6a7ef3bc5f6 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Tue, 14 May 2024 15:41:15 -0700 Subject: [PATCH 48/57] Bug Fix SignedSoftwareStatementBuilder.Build() previously always built using the ES384 Algorithm when a ECC cert was used. This was an oversight. Added a couple tests to help and fixed the code. Found while using UdapEd. --- .../SignedSoftwareStatementBuilder.cs | 13 ++-- .../Basic/ClientCredentialsUdapModeTests.cs | 75 ++++++++++++++++++- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/Udap.Model/Statement/SignedSoftwareStatementBuilder.cs b/Udap.Model/Statement/SignedSoftwareStatementBuilder.cs index f17a1c1c..9a15d4b8 100644 --- a/Udap.Model/Statement/SignedSoftwareStatementBuilder.cs +++ b/Udap.Model/Statement/SignedSoftwareStatementBuilder.cs @@ -40,19 +40,18 @@ public static SignedSoftwareStatementBuilder Create(X509Certificate2 certific // we could add more builder methods // - public string Build(string? algorithm = UdapConstants.SupportedAlgorithm.RS256) + public string Build(string? algorithm = null) { - algorithm ??= UdapConstants.SupportedAlgorithm.RS256; - #if NET5_0_OR_GREATER // // Short circuit to ECDSA // if (_certificate.GetECDsaPublicKey() != null) { - return BuildECDSA(); + return BuildECDSA(algorithm); } #endif + algorithm ??= UdapConstants.SupportedAlgorithm.RS256; var securityKey = new X509SecurityKey(_certificate); var signingCredentials = new SigningCredentials(securityKey, algorithm); @@ -75,11 +74,9 @@ public string Build(string? algorithm = UdapConstants.SupportedAlgorithm.RS256) #if NET5_0_OR_GREATER - public string BuildECDSA(string? algorithm = UdapConstants.SupportedAlgorithm.ES384) + public string BuildECDSA(string? algorithm = null) { - - algorithm ??= UdapConstants.SupportedAlgorithm.ES384; - + algorithm ??= UdapConstants.SupportedAlgorithm.ES256; var key = _certificate.GetECDsaPrivateKey(); using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP384); diff --git a/_tests/UdapServer.Tests/Conformance/Basic/ClientCredentialsUdapModeTests.cs b/_tests/UdapServer.Tests/Conformance/Basic/ClientCredentialsUdapModeTests.cs index bf33a5c3..fb1e2766 100644 --- a/_tests/UdapServer.Tests/Conformance/Basic/ClientCredentialsUdapModeTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Basic/ClientCredentialsUdapModeTests.cs @@ -7,15 +7,15 @@ // */ #endregion + +using System.IdentityModel.Tokens.Jwt; using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Text.Json; -using System.Text.Json.Nodes; using Duende.IdentityServer.Models; -using Duende.IdentityServer.Validation; using FluentAssertions; using IdentityModel; using IdentityModel.Client; @@ -39,6 +39,7 @@ using Udap.Util.Extensions; using UdapServer.Tests.Common; using Xunit.Abstractions; +using JwtHeaderParameterNames = Microsoft.IdentityModel.JsonWebTokens.JwtHeaderParameterNames; namespace UdapServer.Tests.Conformance.Basic; @@ -581,6 +582,9 @@ public async Task GetAccessTokenECDSA() SignedSoftwareStatementBuilder .Create(clientCert, jwtPayload) .BuildECDSA(); + + var jwt = new JwtSecurityToken(clientAssertion); + jwt.Header.Alg.Should().Be(UdapConstants.SupportedAlgorithm.ES256); var clientRequest = new UdapClientCredentialsTokenRequest { @@ -601,6 +605,73 @@ public async Task GetAccessTokenECDSA() } + [Fact] + public async Task GetAccessTokenECDSA_ES384() + { + var clientCert = new X509Certificate2("CertStore/issued/fhirlabs.net.ecdsa.client.pfx", "udap-test", + X509KeyStorageFlags.Exportable); + + var udapClient = _mockPipeline.Resolve(); + + // + // Typically the client would validate a server before proceeding to registration. + // + udapClient.UdapServerMetaData = new UdapMetadata(Substitute.For(), Substitute.For>()) + { RegistrationEndpoint = UdapAuthServerPipeline.RegistrationEndpoint }; + + var regDocumentResult = await udapClient.RegisterClientCredentialsClient( + clientCert, + "system/Patient.rs"); + + regDocumentResult.GetError().Should().BeNull(); + + + // + // Get Access Token + // + var now = DateTime.UtcNow; + var jwtPayload = new JwtPayLoadExtension( + regDocumentResult!.ClientId, + IdentityServerPipeline.TokenEndpoint, + new List() + { + new Claim(JwtClaimTypes.Subject, regDocumentResult.ClientId!), + new Claim(JwtClaimTypes.IssuedAt, EpochTime.GetIntDate(now.ToUniversalTime()).ToString(), + ClaimValueTypes.Integer), + new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId()), + // new Claim(UdapConstants.JwtClaimTypes.Extensions, BuildHl7B2BExtensions() ) //see http://hl7.org/fhir/us/udap-security/b2b.html#constructing-authentication-token + }, + now.ToUniversalTime(), + now.AddMinutes(5).ToUniversalTime() + ); + + var clientAssertion = + SignedSoftwareStatementBuilder + .Create(clientCert, jwtPayload) + .BuildECDSA(UdapConstants.SupportedAlgorithm.ES384); + + var jwt = new JwtSecurityToken(clientAssertion); + jwt.Header.Alg.Should().Be(UdapConstants.SupportedAlgorithm.ES384); + + var clientRequest = new UdapClientCredentialsTokenRequest + { + Address = IdentityServerPipeline.TokenEndpoint, + //ClientId = result.ClientId, we use Implicit ClientId in the iss claim + ClientAssertion = new ClientAssertion() + { + Type = OidcConstants.ClientAssertionTypes.JwtBearer, + Value = clientAssertion + }, + Udap = UdapConstants.UdapVersionsSupportedValue, + Scope = "system/Patient.rs" + }; + + var tokenResponse = await _mockPipeline.BackChannelClient.UdapRequestClientCredentialsTokenAsync(clientRequest); + + tokenResponse.Scope.Should().Be("system/Patient.rs", tokenResponse.Raw); + + } + [Fact] public async Task UpdateRegistration() { From c4ade9a2fd8620f4127a18637babae678ef6c9e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 01:09:20 +0000 Subject: [PATCH 49/57] Bump Microsoft.Extensions.Http.Polly from 8.0.4 to 8.0.5 Bumps [Microsoft.Extensions.Http.Polly](https://github.com/dotnet/aspnetcore) from 8.0.4 to 8.0.5. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.4...v8.0.5) --- updated-dependencies: - dependency-name: Microsoft.Extensions.Http.Polly dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj index 4e1a4ca9..e774ee19 100644 --- a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj +++ b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj @@ -17,7 +17,7 @@ - + From 1be9a7ca9921ab3aa3062dce676443a55d543cbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 01:30:26 +0000 Subject: [PATCH 50/57] Bump Microsoft.AspNetCore.DataProtection.EntityFrameworkCore, Microsoft.AspNetCore.DataProtection.Abstractions and Microsoft.EntityFrameworkCore Bumps [Microsoft.AspNetCore.DataProtection.EntityFrameworkCore](https://github.com/dotnet/aspnetcore), [Microsoft.AspNetCore.DataProtection.Abstractions](https://github.com/dotnet/aspnetcore) and [Microsoft.EntityFrameworkCore](https://github.com/dotnet/efcore). These dependencies needed to be updated together. Updates `Microsoft.AspNetCore.DataProtection.EntityFrameworkCore` from 8.0.4 to 8.0.5 - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.4...v8.0.5) Updates `Microsoft.AspNetCore.DataProtection.Abstractions` from 8.0.4 to 8.0.5 - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.4...v8.0.5) Updates `Microsoft.EntityFrameworkCore` from 8.0.4 to 8.0.5 - [Release notes](https://github.com/dotnet/efcore/releases) - [Commits](https://github.com/dotnet/efcore/compare/v8.0.4...v8.0.5) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.DataProtection.EntityFrameworkCore dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Microsoft.AspNetCore.DataProtection.Abstractions dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Microsoft.EntityFrameworkCore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ae53bd97..83f6266c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,8 +11,8 @@ - - + + @@ -27,7 +27,7 @@ - + From 8ef5979675ff218f907a71f4d7b013a930b8b9fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 01:32:08 +0000 Subject: [PATCH 51/57] Bump Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore Bumps [Microsoft.EntityFrameworkCore.SqlServer](https://github.com/dotnet/efcore) and [Microsoft.EntityFrameworkCore](https://github.com/dotnet/efcore). These dependencies needed to be updated together. Updates `Microsoft.EntityFrameworkCore.SqlServer` from 8.0.4 to 8.0.5 - [Release notes](https://github.com/dotnet/efcore/releases) - [Commits](https://github.com/dotnet/efcore/compare/v8.0.4...v8.0.5) Updates `Microsoft.EntityFrameworkCore` from 8.0.4 to 8.0.5 - [Release notes](https://github.com/dotnet/efcore/releases) - [Commits](https://github.com/dotnet/efcore/compare/v8.0.4...v8.0.5) --- updated-dependencies: - dependency-name: Microsoft.EntityFrameworkCore.SqlServer dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Microsoft.EntityFrameworkCore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj | 4 ++-- examples/Udap.Auth.Server/Udap.Auth.Server.csproj | 2 +- examples/Udap.CA/Udap.CA.csproj | 2 +- migrations/UdapDb.SqlServer/UdapDb.SqlServer.csproj | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ae53bd97..274dbc83 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -27,7 +27,7 @@ - + diff --git a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj index e774ee19..b77a2038 100644 --- a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj +++ b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj @@ -13,9 +13,9 @@ - + - + diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index 723d0b4a..0de646bf 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -23,7 +23,7 @@ all - + diff --git a/examples/Udap.CA/Udap.CA.csproj b/examples/Udap.CA/Udap.CA.csproj index a0b44403..d0776e05 100644 --- a/examples/Udap.CA/Udap.CA.csproj +++ b/examples/Udap.CA/Udap.CA.csproj @@ -17,7 +17,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/migrations/UdapDb.SqlServer/UdapDb.SqlServer.csproj b/migrations/UdapDb.SqlServer/UdapDb.SqlServer.csproj index b075d454..8068d2c1 100644 --- a/migrations/UdapDb.SqlServer/UdapDb.SqlServer.csproj +++ b/migrations/UdapDb.SqlServer/UdapDb.SqlServer.csproj @@ -13,12 +13,12 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 7aa81141e017583b06f838583b825df154f0cadf Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Tue, 14 May 2024 19:27:22 -0700 Subject: [PATCH 52/57] Package updates --- Directory.Packages.props | 2 +- _tests/Directory.Packages.props | 14 +++++++------- examples/FhirLabsApi/FhirLabsApi.csproj | 6 +++--- .../Udap.Auth.Server.Admin.csproj | 4 ++-- examples/Udap.Auth.Server/Udap.Auth.Server.csproj | 4 ++-- examples/Udap.CA/Udap.CA.csproj | 8 ++++---- .../Udap.Identity.Provider.2.csproj | 8 ++++---- .../Udap.Identity.Provider.csproj | 8 ++++---- .../Udap.Proxy.Server/Udap.Proxy.Server.csproj | 6 +++--- .../1_UdapClientMetadata.csproj | 2 +- .../2_UdapClientMetadata.csproj | 2 +- migrations/UdapDb.Postgres/UdapDb.Postgres.csproj | 6 +++--- .../UdapDb.SqlServer/UdapDb.SqlServer.csproj | 6 +++--- 13 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 83f6266c..e7cf0682 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,7 +9,7 @@ - + diff --git a/_tests/Directory.Packages.props b/_tests/Directory.Packages.props index 9242f559..a39619ab 100644 --- a/_tests/Directory.Packages.props +++ b/_tests/Directory.Packages.props @@ -6,18 +6,18 @@ - - + + - + - - - - + + + + diff --git a/examples/FhirLabsApi/FhirLabsApi.csproj b/examples/FhirLabsApi/FhirLabsApi.csproj index 0e235876..e16011e6 100644 --- a/examples/FhirLabsApi/FhirLabsApi.csproj +++ b/examples/FhirLabsApi/FhirLabsApi.csproj @@ -45,9 +45,9 @@ - - - + + + diff --git a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj index b77a2038..e627755a 100644 --- a/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj +++ b/examples/Udap.Auth.Server.Admin/Udap.Auth.Server.Admin.csproj @@ -12,9 +12,9 @@ - + - + diff --git a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj index 0de646bf..c05b4684 100644 --- a/examples/Udap.Auth.Server/Udap.Auth.Server.csproj +++ b/examples/Udap.Auth.Server/Udap.Auth.Server.csproj @@ -18,11 +18,11 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/examples/Udap.CA/Udap.CA.csproj b/examples/Udap.CA/Udap.CA.csproj index d0776e05..699f89e0 100644 --- a/examples/Udap.CA/Udap.CA.csproj +++ b/examples/Udap.CA/Udap.CA.csproj @@ -11,14 +11,14 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj b/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj index 034da325..06226cc8 100644 --- a/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj +++ b/examples/Udap.Identity.Provider.2/Udap.Identity.Provider.2.csproj @@ -21,14 +21,14 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - + diff --git a/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj b/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj index 07e5ba39..0149f822 100644 --- a/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj +++ b/examples/Udap.Identity.Provider/Udap.Identity.Provider.csproj @@ -21,14 +21,14 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - + diff --git a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj index 509e0d54..a366cceb 100644 --- a/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj +++ b/examples/Udap.Proxy.Server/Udap.Proxy.Server.csproj @@ -14,10 +14,10 @@ - - + + - + diff --git a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj index dfae43ac..68f0876b 100644 --- a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj +++ b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + diff --git a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj index 5f4146bf..4232c0f6 100644 --- a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj +++ b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + diff --git a/migrations/UdapDb.Postgres/UdapDb.Postgres.csproj b/migrations/UdapDb.Postgres/UdapDb.Postgres.csproj index aa443c2b..8594cb5b 100644 --- a/migrations/UdapDb.Postgres/UdapDb.Postgres.csproj +++ b/migrations/UdapDb.Postgres/UdapDb.Postgres.csproj @@ -13,9 +13,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/migrations/UdapDb.SqlServer/UdapDb.SqlServer.csproj b/migrations/UdapDb.SqlServer/UdapDb.SqlServer.csproj index 8068d2c1..3826757b 100644 --- a/migrations/UdapDb.SqlServer/UdapDb.SqlServer.csproj +++ b/migrations/UdapDb.SqlServer/UdapDb.SqlServer.csproj @@ -12,14 +12,14 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 7476a6e3f033e7f860f6bc1091d06d70e1fbca11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 12:44:03 +0000 Subject: [PATCH 53/57] Bump Udap.Client, Udap.Common and Udap.Util Bumps [Udap.Client](https://github.com/JoeShook/udap-dotnet), Udap.Common and Udap.Util. These dependencies needed to be updated together. Updates `Udap.Client` from 0.3.46 to 0.3.47 - [Commits](https://github.com/JoeShook/udap-dotnet/compare/v0.3.46...v0.3.47) Updates `Udap.Common` from 0.3.46 to 0.3.47 Updates `Udap.Util` from 0.3.46 to 0.3.47 --- updated-dependencies: - dependency-name: Udap.Client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Common dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: Udap.Util dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj | 2 +- .../clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj index 68f0876b..06638fdf 100644 --- a/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj +++ b/examples/clients/1_UdapClientMetadata/1_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + diff --git a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj index 4232c0f6..5da31d50 100644 --- a/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj +++ b/examples/clients/2_UdapClientMetadata/2_UdapClientMetadata.csproj @@ -37,7 +37,7 @@ - + From 13a5ad7db6bc41d63242a8cd63aab0b5a9f1e5a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 12:51:41 +0000 Subject: [PATCH 54/57] Bump Microsoft.IdentityModel.Protocols.OpenIdConnect from 7.5.1 to 7.5.2 Bumps [Microsoft.IdentityModel.Protocols.OpenIdConnect](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 7.5.1 to 7.5.2. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/7.5.1...7.5.2) --- updated-dependencies: - dependency-name: Microsoft.IdentityModel.Protocols.OpenIdConnect dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index e7cf0682..5ea32d2d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -15,7 +15,7 @@ - + From 2a44b52c78d1f24f8b0ad1283540a2727ae23a0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 12:56:03 +0000 Subject: [PATCH 55/57] Bump Microsoft.AspNetCore.Authentication.OpenIdConnect Bumps [Microsoft.AspNetCore.Authentication.OpenIdConnect](https://github.com/dotnet/aspnetcore) from 8.0.4 to 8.0.5. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md) - [Commits](https://github.com/dotnet/aspnetcore/compare/v8.0.4...v8.0.5) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.Authentication.OpenIdConnect dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index e7cf0682..538af8c8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,7 +10,7 @@ - + From b5f095a0b1216755abef2c3232fa93a8e8a6de79 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Wed, 15 May 2024 12:43:19 -0700 Subject: [PATCH 56/57] Create ssl certs for HL7.org FAST Reference Implemenations. Aid in Touchstone testing. --- .../BuildNginxProxySSLCerts.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/_tests/Udap.PKI.Generator/BuildNginxProxySSLCerts.cs b/_tests/Udap.PKI.Generator/BuildNginxProxySSLCerts.cs index 9f9bc2a6..b526f834 100644 --- a/_tests/Udap.PKI.Generator/BuildNginxProxySSLCerts.cs +++ b/_tests/Udap.PKI.Generator/BuildNginxProxySSLCerts.cs @@ -176,8 +176,31 @@ public static IEnumerable SSLProxyCerts() }; } + + public static IEnumerable Hl7SRI() + { + yield return new object[] + { + "CN=identity-matching.fast.hl7.org", //DistinguishedName + "identity-matching.fast.hl7.org" //SubjAltName + }; + + yield return new object[] + { + "CN=national-directory.fast.hl7.org", //DistinguishedName + "national-directory.fast.hl7.org" //SubjAltName + }; + + yield return new object[] + { + "CN=udap-security.fast.hl7.org", //DistinguishedName + "udap-security.fast.hl7.org" //SubjAltName + }; + } + [Theory(Skip = "Enabled on desktop when needed.")] [MemberData(nameof(SSLProxyCerts))] + [MemberData(nameof(Hl7SRI))] public void MakeIdentityProviderCertificates(string dn, string san) { using var rootCA = new X509Certificate2($"{SureFhirLabsCertStore}/ngnix-proxy-TestCA.pfx", "udap-test"); @@ -193,6 +216,8 @@ public void MakeIdentityProviderCertificates(string dn, string san) } + + private X509Certificate2 BuildSslCertificate( X509Certificate2? caCert, string distinguishedName, From df985ba66ffcdc163156cce08e44e48015e600b2 Mon Sep 17 00:00:00 2001 From: Joseph Shook Date: Thu, 16 May 2024 15:16:35 -0700 Subject: [PATCH 57/57] Changed udap scope from IdentityResource to ApiScope type. That way the auth server does not require oidc scope if udap is passed. Any IdentityResource type must also sent the oidc scope according to default IdentityServer rules. Of source they must both be used when calling the IdP in a Tiered Oauth flow. But that is a different rule. I just didn't think udap scope should carry the Identity semantic meaning. --- Udap.Server/Models/UdapIdentityResources.cs | 5 +- .../Conformance/Basic/ScopeExpansionTests.cs | 2 +- .../Basic/UdapForceStateParamFalseTests.cs | 2 +- .../Conformance/Tiered/TieredOauthTests.cs | 9 ++- _tests/UdapServer.Tests/SeedData.cs | 6 +- .../Properties/launchSettings.json | 11 +-- examples/Udap.Auth.Server.Admin/SeedData.cs | 1 - .../UdapDb.Postgres/SeedData.Auth.Server.cs | 8 +- .../SeedData.Identity.Provider.cs | 6 +- .../SeedData.Identity.Provider2.cs | 6 +- .../UdapDb.Postgres/Seed_GCP_Auth_Server.cs | 81 +++++++------------ migrations/UdapDb.Postgres/Seed_GCP_Idp1.cs | 6 +- migrations/UdapDb.Postgres/Seed_GCP_Idp2.cs | 6 +- .../UdapDb.SqlServer/SeedData.Auth.Server.cs | 8 +- .../SeedData.Identity.Provider.cs | 6 +- .../SeedData.Identity.Provider2.cs | 6 +- .../UdapDb.SqlServer/Seed_GCP_Auth_Server.cs | 8 +- migrations/UdapDb.SqlServer/Seed_GCP_Idp1.cs | 6 +- migrations/UdapDb.SqlServer/Seed_GCP_Idp2.cs | 6 +- 19 files changed, 82 insertions(+), 107 deletions(-) diff --git a/Udap.Server/Models/UdapIdentityResources.cs b/Udap.Server/Models/UdapIdentityResources.cs index 1e6e86b1..10c4163c 100644 --- a/Udap.Server/Models/UdapIdentityResources.cs +++ b/Udap.Server/Models/UdapIdentityResources.cs @@ -56,8 +56,11 @@ public Profile() UserClaims.Add(UdapConstants.JwtClaimTypes.Hl7Identifier); } } +} - public class Udap : IdentityResource +public static class UdapApiScopes +{ + public class Udap : ApiScope { /// /// Initializes a new instance of the class. diff --git a/_tests/UdapServer.Tests/Conformance/Basic/ScopeExpansionTests.cs b/_tests/UdapServer.Tests/Conformance/Basic/ScopeExpansionTests.cs index 8a444c91..451e5955 100644 --- a/_tests/UdapServer.Tests/Conformance/Basic/ScopeExpansionTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Basic/ScopeExpansionTests.cs @@ -122,7 +122,7 @@ public ScopeExpansionTests(ITestOutputHelper testOutputHelper) _mockPipeline.IdentityScopes.Add(new IdentityResources.OpenId()); - _mockPipeline.IdentityScopes.Add(new UdapIdentityResources.Udap()); + _mockPipeline.ApiScopes.Add(new UdapApiScopes.Udap()); _mockPipeline.Users.Add(new TestUser { diff --git a/_tests/UdapServer.Tests/Conformance/Basic/UdapForceStateParamFalseTests.cs b/_tests/UdapServer.Tests/Conformance/Basic/UdapForceStateParamFalseTests.cs index 32fab76a..592b71d7 100644 --- a/_tests/UdapServer.Tests/Conformance/Basic/UdapForceStateParamFalseTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Basic/UdapForceStateParamFalseTests.cs @@ -113,7 +113,7 @@ public UdapForceStateParamFalseTests(ITestOutputHelper testOutputHelper) _mockPipeline.IdentityScopes.Add(new IdentityResources.OpenId()); _mockPipeline.IdentityScopes.Add(new IdentityResources.Profile()); - _mockPipeline.IdentityScopes.Add(new UdapIdentityResources.Udap()); + _mockPipeline.ApiScopes.Add(new UdapApiScopes.Udap()); _mockPipeline.Users.Add(new TestUser { diff --git a/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs b/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs index 2069c98b..3489b4bf 100644 --- a/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs +++ b/_tests/UdapServer.Tests/Conformance/Tiered/TieredOauthTests.cs @@ -218,7 +218,7 @@ private void BuildUdapAuthorizationServer(List? tieredOAuthScopes = null _mockAuthorServerPipeline.IdentityScopes.Add(new IdentityResources.OpenId()); _mockAuthorServerPipeline.IdentityScopes.Add(new IdentityResources.Profile()); - _mockAuthorServerPipeline.IdentityScopes.Add(new UdapIdentityResources.Udap()); + _mockAuthorServerPipeline.ApiScopes.Add(new UdapApiScopes.Udap()); _mockAuthorServerPipeline.ApiScopes.Add(new ApiScope("user/*.read")); @@ -305,7 +305,7 @@ private void BuildUdapIdentityProvider1() _mockIdPPipeline.IdentityScopes.Add(new IdentityResources.OpenId()); _mockIdPPipeline.IdentityScopes.Add(new UdapIdentityResources.Profile()); - _mockIdPPipeline.IdentityScopes.Add(new UdapIdentityResources.Udap()); + _mockIdPPipeline.ApiScopes.Add(new UdapApiScopes.Udap()); _mockIdPPipeline.IdentityScopes.Add(new IdentityResources.Email()); _mockIdPPipeline.IdentityScopes.Add(new UdapIdentityResources.FhirUser()); @@ -395,7 +395,7 @@ private void BuildUdapIdentityProvider2() _mockIdPPipeline2.IdentityScopes.Add(new IdentityResources.OpenId()); _mockIdPPipeline2.IdentityScopes.Add(new UdapIdentityResources.Profile()); - _mockIdPPipeline2.IdentityScopes.Add(new UdapIdentityResources.Udap()); + _mockIdPPipeline2.ApiScopes.Add(new UdapApiScopes.Udap()); _mockIdPPipeline2.IdentityScopes.Add(new IdentityResources.Email()); _mockIdPPipeline2.IdentityScopes.Add(new UdapIdentityResources.FhirUser()); @@ -446,7 +446,8 @@ public async Task ClientAuthorize_IdPDiscovery_IdPRegistration_IdPAuthAccess_Cli // Data Holder's Auth Server validates Identity Provider's Server software statement var clientState = Guid.NewGuid().ToString(); - + + // Builds https://server/connect/authorize plus query params var clientAuthorizeUrl = _mockAuthorServerPipeline.CreateAuthorizeUrl( clientId: clientId, responseType: "code", diff --git a/_tests/UdapServer.Tests/SeedData.cs b/_tests/UdapServer.Tests/SeedData.cs index fc48adb9..030121f3 100644 --- a/_tests/UdapServer.Tests/SeedData.cs +++ b/_tests/UdapServer.Tests/SeedData.cs @@ -226,7 +226,6 @@ private static async Task SeedFhirScopes( { var apiScopes = configDbContext.ApiScopes .Include(s => s.Properties) - .Where(s => s.Enabled) .Select(s => s) .ToList(); @@ -241,6 +240,7 @@ private static async Task SeedFhirScopes( if (apiScope.Name.StartsWith("system/*.")) { apiScope.ShowInDiscoveryDocument = true; + apiScope.Enabled = false; } apiScope.Properties.Add("udap_prefix", "system"); @@ -262,9 +262,10 @@ private static async Task SeedFhirScopes( var apiScope = new ApiScope(scopeName); apiScope.ShowInDiscoveryDocument = false; - if (apiScope.Name.StartsWith("patient/*.")) + if (apiScope.Name.StartsWith("user/*.")) { apiScope.ShowInDiscoveryDocument = true; + apiScope.Enabled = false; } apiScope.Properties.Add("udap_prefix", "user"); @@ -288,6 +289,7 @@ private static async Task SeedFhirScopes( if (apiScope.Name.StartsWith("patient/*.")) { apiScope.ShowInDiscoveryDocument = true; + apiScope.Enabled = false; } apiScope.Properties.Add("udap_prefix", "patient"); diff --git a/examples/Udap.Auth.Server.Admin/Properties/launchSettings.json b/examples/Udap.Auth.Server.Admin/Properties/launchSettings.json index 9b9f550e..a8677490 100644 --- a/examples/Udap.Auth.Server.Admin/Properties/launchSettings.json +++ b/examples/Udap.Auth.Server.Admin/Properties/launchSettings.json @@ -18,16 +18,7 @@ }, "dotnetRunMessages": true, "applicationUrl": "http://localhost:5253" - }, - "SecuredControls": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "sslPort": 5002, - "applicationUrl": "https://admin.securedcontrols.net:5002" - }, + }, "/seed": { "commandName": "Project", "commandLineArgs": "/seed ../../../../../_tests/Udap.PKI.Generator/certstores", diff --git a/examples/Udap.Auth.Server.Admin/SeedData.cs b/examples/Udap.Auth.Server.Admin/SeedData.cs index 719515e4..b8bf579d 100644 --- a/examples/Udap.Auth.Server.Admin/SeedData.cs +++ b/examples/Udap.Auth.Server.Admin/SeedData.cs @@ -168,7 +168,6 @@ public static void EnsureSeedData(string connectionString, string certStoreBaseP } var apiScopes = configDbContext.ApiScopes - .Where(s => s.Enabled) .Select(s => s.Name) .ToList(); diff --git a/migrations/UdapDb.Postgres/SeedData.Auth.Server.cs b/migrations/UdapDb.Postgres/SeedData.Auth.Server.cs index ff33c77c..5da03710 100644 --- a/migrations/UdapDb.Postgres/SeedData.Auth.Server.cs +++ b/migrations/UdapDb.Postgres/SeedData.Auth.Server.cs @@ -433,10 +433,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } @@ -541,7 +541,7 @@ private static async Task SeedFhirScopes( var apiScope = new ApiScope(scopeName); apiScope.ShowInDiscoveryDocument = false; - if (apiScope.Name.StartsWith("patient/*.")) + if (apiScope.Name.StartsWith("user/*.")) { apiScope.ShowInDiscoveryDocument = true; apiScope.Enabled = false; diff --git a/migrations/UdapDb.Postgres/SeedData.Identity.Provider.cs b/migrations/UdapDb.Postgres/SeedData.Identity.Provider.cs index d6a0df1d..e4c06d3b 100644 --- a/migrations/UdapDb.Postgres/SeedData.Identity.Provider.cs +++ b/migrations/UdapDb.Postgres/SeedData.Identity.Provider.cs @@ -246,10 +246,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } diff --git a/migrations/UdapDb.Postgres/SeedData.Identity.Provider2.cs b/migrations/UdapDb.Postgres/SeedData.Identity.Provider2.cs index d94bd004..f42b0974 100644 --- a/migrations/UdapDb.Postgres/SeedData.Identity.Provider2.cs +++ b/migrations/UdapDb.Postgres/SeedData.Identity.Provider2.cs @@ -183,10 +183,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } diff --git a/migrations/UdapDb.Postgres/Seed_GCP_Auth_Server.cs b/migrations/UdapDb.Postgres/Seed_GCP_Auth_Server.cs index 75d68303..d8f66c75 100644 --- a/migrations/UdapDb.Postgres/Seed_GCP_Auth_Server.cs +++ b/migrations/UdapDb.Postgres/Seed_GCP_Auth_Server.cs @@ -48,19 +48,22 @@ public static async Task EnsureSeedData(string connectionString, string cer services.AddOperationalDbContext(options => { options.ConfigureDbContext = db => db.UseNpgsql(connectionString, - sql => sql.MigrationsAssembly(typeof(Program).Assembly.FullName)); + sql => sql.MigrationsAssembly(typeof(Program).Assembly.FullName) + .MigrationsHistoryTable("__migrations_history", "udap")); }); services.AddConfigurationDbContext(options => { options.ConfigureDbContext = db => db.UseNpgsql(connectionString, - sql => sql.MigrationsAssembly(typeof(Program).Assembly.FullName)); + sql => sql.MigrationsAssembly(typeof(Program).Assembly.FullName) + .MigrationsHistoryTable("__migrations_history", "udap")); }); services.AddScoped(); services.AddUdapDbContext(options => { options.UdapDbContext = db => db.UseNpgsql(connectionString, - sql => sql.MigrationsAssembly(typeof(Program).Assembly.FullName)); + sql => sql.MigrationsAssembly(typeof(Program).Assembly.FullName) + .MigrationsHistoryTable("__migrations_history", "udap")); }); await using var serviceProvider = services.BuildServiceProvider(); @@ -75,12 +78,12 @@ public static async Task EnsureSeedData(string connectionString, string cer var clientRegistrationStore = serviceScope.ServiceProvider.GetRequiredService(); - - if (!udapContext.Communities.Any(c => c.Name == "http://localhost")) + + if (!udapContext.Communities.Any(c => c.Name == "udap://stage.healthtogo.me/")) { - var community = new Community { Name = "http://localhost" }; + var community = new Community { Name = "udap://stage.healthtogo.me/" }; community.Enabled = true; - community.Default = false; + community.Default = true; udapContext.Communities.Add(community); await udapContext.SaveChangesAsync(); } @@ -89,7 +92,7 @@ public static async Task EnsureSeedData(string connectionString, string cer { var community = new Community { Name = "udap://fhirlabs.net/" }; community.Enabled = true; - community.Default = true; + community.Default = false; udapContext.Communities.Add(community); await udapContext.SaveChangesAsync(); } @@ -156,56 +159,32 @@ public static async Task EnsureSeedData(string connectionString, string cer // - // Anchor localhost_community + // Anchor for Community udap://stage.healthtogo.me/ // - var anchorLocalhostCert = new X509Certificate2( - Path.Combine(assemblyPath!, certStoreBasePath, "localhost_fhirlabs_community1/caLocalhostCert.cer")); + var emrDirectTestCA = new X509Certificate2( + Path.Combine(assemblyPath!, certStoreBasePath, "EmrDirect/EMRDirectTestCA.crt")); - if ((await clientRegistrationStore.GetAnchors("http://localhost")) - .All(a => a.Thumbprint != anchorLocalhostCert.Thumbprint)) + if ((await clientRegistrationStore.GetAnchors("udap://stage.healthtogo.me/")) + .All(a => a.Thumbprint != emrDirectTestCA.Thumbprint)) { - var community = udapContext.Communities.Single(c => c.Name == "http://localhost"); - var anchor = new Anchor + var community = udapContext.Communities.Single(c => c.Name == "udap://stage.healthtogo.me/"); + + anchor = new Anchor { - BeginDate = anchorLocalhostCert.NotBefore.ToUniversalTime(), - EndDate = anchorLocalhostCert.NotAfter.ToUniversalTime(), - Name = anchorLocalhostCert.Subject, + BeginDate = emrDirectTestCA.NotBefore.ToUniversalTime(), + EndDate = emrDirectTestCA.NotAfter.ToUniversalTime(), + Name = emrDirectTestCA.Subject, Community = community, - X509Certificate = anchorLocalhostCert.ToPemFormat(), - Thumbprint = anchorLocalhostCert.Thumbprint, + X509Certificate = emrDirectTestCA.ToPemFormat(), + Thumbprint = emrDirectTestCA.Thumbprint, Enabled = true }; - udapContext.Anchors.Add(anchor); + udapContext.Anchors.Add(anchor); await udapContext.SaveChangesAsync(); - - // - // Intermediate surefhirlabs_community - // - var x509Certificate2Collection = await clientRegistrationStore.GetIntermediateCertificates(); - - intermediateCert = new X509Certificate2( - Path.Combine(assemblyPath!, certStoreBasePath, "localhost_fhirlabs_community1/intermediates/intermediateLocalhostCert.cer")); - - if (x509Certificate2Collection != null && x509Certificate2Collection.ToList() - .All(r => r.Thumbprint != intermediateCert.Thumbprint)) - { - - udapContext.IntermediateCertificates.Add(new Intermediate - { - BeginDate = intermediateCert.NotBefore.ToUniversalTime(), - EndDate = intermediateCert.NotAfter.ToUniversalTime(), - Name = intermediateCert.Subject, - X509Certificate = intermediateCert.ToPemFormat(), - Thumbprint = intermediateCert.Thumbprint, - Enabled = true, - Anchor = anchor - }); - - await udapContext.SaveChangesAsync(); - } } + Func treatmentSpecification = r => r is "Patient" or "AllergyIntolerance" or "Condition" or "Encounter"; await SeedFhirScopes(configDbContext, Hl7ModelInfoExtensions.BuildHl7FhirV1Scopes("patient", treatmentSpecification), 1); @@ -242,10 +221,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } @@ -308,7 +287,7 @@ private static async Task SeedFhirScopes(NpgsqlConfigurationDbContext configDbCo { var apiScope = new ApiScope(scopeName); apiScope.ShowInDiscoveryDocument = false; - if (apiScope.Name.StartsWith("patient/*.")) + if (apiScope.Name.StartsWith("user/*.")) { apiScope.ShowInDiscoveryDocument = true; apiScope.Enabled = false; diff --git a/migrations/UdapDb.Postgres/Seed_GCP_Idp1.cs b/migrations/UdapDb.Postgres/Seed_GCP_Idp1.cs index bb2064f3..ac007397 100644 --- a/migrations/UdapDb.Postgres/Seed_GCP_Idp1.cs +++ b/migrations/UdapDb.Postgres/Seed_GCP_Idp1.cs @@ -146,10 +146,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } diff --git a/migrations/UdapDb.Postgres/Seed_GCP_Idp2.cs b/migrations/UdapDb.Postgres/Seed_GCP_Idp2.cs index a015fbbb..eb132c7f 100644 --- a/migrations/UdapDb.Postgres/Seed_GCP_Idp2.cs +++ b/migrations/UdapDb.Postgres/Seed_GCP_Idp2.cs @@ -238,10 +238,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } diff --git a/migrations/UdapDb.SqlServer/SeedData.Auth.Server.cs b/migrations/UdapDb.SqlServer/SeedData.Auth.Server.cs index 39fc440d..5ea21a3a 100644 --- a/migrations/UdapDb.SqlServer/SeedData.Auth.Server.cs +++ b/migrations/UdapDb.SqlServer/SeedData.Auth.Server.cs @@ -433,10 +433,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } @@ -523,7 +523,7 @@ private static async Task SeedFhirScopes( var apiScope = new ApiScope(scopeName); apiScope.ShowInDiscoveryDocument = false; - if (apiScope.Name.StartsWith("patient/*.")) + if (apiScope.Name.StartsWith("user/*.")) { apiScope.ShowInDiscoveryDocument = true; apiScope.Enabled = false; diff --git a/migrations/UdapDb.SqlServer/SeedData.Identity.Provider.cs b/migrations/UdapDb.SqlServer/SeedData.Identity.Provider.cs index 765649b5..af365bfd 100644 --- a/migrations/UdapDb.SqlServer/SeedData.Identity.Provider.cs +++ b/migrations/UdapDb.SqlServer/SeedData.Identity.Provider.cs @@ -246,10 +246,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } diff --git a/migrations/UdapDb.SqlServer/SeedData.Identity.Provider2.cs b/migrations/UdapDb.SqlServer/SeedData.Identity.Provider2.cs index e5a61337..5baf2c93 100644 --- a/migrations/UdapDb.SqlServer/SeedData.Identity.Provider2.cs +++ b/migrations/UdapDb.SqlServer/SeedData.Identity.Provider2.cs @@ -183,10 +183,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } diff --git a/migrations/UdapDb.SqlServer/Seed_GCP_Auth_Server.cs b/migrations/UdapDb.SqlServer/Seed_GCP_Auth_Server.cs index 618cb6d0..f869a0f0 100644 --- a/migrations/UdapDb.SqlServer/Seed_GCP_Auth_Server.cs +++ b/migrations/UdapDb.SqlServer/Seed_GCP_Auth_Server.cs @@ -242,10 +242,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } @@ -322,7 +322,7 @@ private static async Task SeedFhirScopes(ConfigurationDbContext configDbContext, { var apiScope = new ApiScope(scopeName); apiScope.ShowInDiscoveryDocument = false; - if (apiScope.Name.StartsWith("patient/*.")) + if (apiScope.Name.StartsWith("user/*.")) { apiScope.ShowInDiscoveryDocument = true; apiScope.Enabled = false; diff --git a/migrations/UdapDb.SqlServer/Seed_GCP_Idp1.cs b/migrations/UdapDb.SqlServer/Seed_GCP_Idp1.cs index 4e156872..747f17b2 100644 --- a/migrations/UdapDb.SqlServer/Seed_GCP_Idp1.cs +++ b/migrations/UdapDb.SqlServer/Seed_GCP_Idp1.cs @@ -146,10 +146,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); } diff --git a/migrations/UdapDb.SqlServer/Seed_GCP_Idp2.cs b/migrations/UdapDb.SqlServer/Seed_GCP_Idp2.cs index 3d1873a9..9e918845 100644 --- a/migrations/UdapDb.SqlServer/Seed_GCP_Idp2.cs +++ b/migrations/UdapDb.SqlServer/Seed_GCP_Idp2.cs @@ -238,10 +238,10 @@ public static async Task EnsureSeedData(string connectionString, string cer // // udap // - if (configDbContext.IdentityResources.All(i => i.Name != UdapConstants.StandardScopes.Udap)) + if (configDbContext.ApiScopes.All(i => i.Name != UdapConstants.StandardScopes.Udap)) { - var udapIdentity = new UdapIdentityResources.Udap(); - configDbContext.IdentityResources.Add(udapIdentity.ToEntity()); + var udapIdentity = new UdapApiScopes.Udap(); + configDbContext.ApiScopes.Add(udapIdentity.ToEntity()); await configDbContext.SaveChangesAsync(); }