From 384a60ac2d8e336d5d13ae35936901240eb9534e Mon Sep 17 00:00:00 2001 From: Maarten Balliauw Date: Thu, 17 Apr 2025 15:49:31 +0200 Subject: [PATCH 1/5] How to have dynamically configured schemes at runtime? #662 --- .../endpoints/dynamic-registration.md | 3 +- .../ui/login/dynamicproviders.md | 396 ++++++++++++++---- 2 files changed, 322 insertions(+), 77 deletions(-) diff --git a/src/content/docs/identitymodel/endpoints/dynamic-registration.md b/src/content/docs/identitymodel/endpoints/dynamic-registration.md index 2d92858d7..690f7aad5 100644 --- a/src/content/docs/identitymodel/endpoints/dynamic-registration.md +++ b/src/content/docs/identitymodel/endpoints/dynamic-registration.md @@ -8,8 +8,7 @@ redirect_from: - /foss/identitymodel/endpoints/dynamic_registration/ --- -The client library for [OpenID Connect Dynamic Client -Registration](https://openid.net/specs/openid-connect-registration-1_0.html) +The client library for [OpenID Connect Dynamic Client Registration](https://openid.net/specs/openid-connect-registration-1_0.html) is provided as an extension method for *HttpClient*. The following code sends a registration request: diff --git a/src/content/docs/identityserver/ui/login/dynamicproviders.md b/src/content/docs/identityserver/ui/login/dynamicproviders.md index 301d979f3..8d189d98e 100644 --- a/src/content/docs/identityserver/ui/login/dynamicproviders.md +++ b/src/content/docs/identityserver/ui/login/dynamicproviders.md @@ -9,30 +9,112 @@ redirect_from: - /identityserver/v7/ui/login/dynamicproviders/ --- -Dynamic Identity Providers are a scalable solution for managing authentication with numerous external providers without +Dynamic Identity Providers are a scalable solution for managing authentication with lots of external providers, without incurring performance penalties or requiring application recompilation. This feature, included in the Enterprise Edition of Duende IdentityServer, enables providers to be configured dynamically from a store at runtime. ## Dynamic Identity Providers -Normally authentication handlers for external providers are added into your IdentityServer using `AddAuthentication()` -and `AddOpenIdConnect()`. This is fine for a handful of schemes, but the authentication handler architecture in ASP.NET -Core was not designed for dozens or more statically registered in the DI system. At some point you will incur a -performance penalty for having too many. Also, as you need to add or change this configuration you will need to -re-compile and re-run your startup code for those changes to take effect. +Authentication handlers for external providers are typically added into your IdentityServer using `AddAuthentication()` +and `AddOpenIdConnect()`. This is fine for a handful of schemes, but becomes harder to manage if you have too many of them. +Additionally, you'd have to re-compile and re-run your startup code for new authentication handlers to be picked up by ASP.NET Core. -Duende IdentityServer provides support for dynamic configuration of OpenID Connect providers loaded from a store. This -is designed to address the performance concern and allowing changes to the configuration to a running server. +The authentication handler architecture in ASP.NET Core was not designed to have many statically registered authentication +handlers registered in the service container and Dependency Injection (DI) system. At some point you will incur a +performance penalty for having too many of them. -Support for Dynamic Identity Providers is included -in [IdentityServer](https://duendesoftware.com/products/identityserver) Enterprise Edition. +Duende IdentityServer provides support for dynamic configuration of OpenID Connect providers loaded from a store. +Dynamic configuration addresses the performance concern and allows changes to the configuration to a running server. -### Listing Dynamic Providers On The Login Page +Support for Dynamic Identity Providers is included in the [Duende IdentityServer](https://duendesoftware.com/products/identityserver) Enterprise Edition. -The [identity provider store](/identityserver/reference/stores/idp-store/) can be used to query the database +## Store And Configuration Data + +Dynamic identity providers are configured in IdentityServer and require a store for the configuration data of [dynamic OIDC providers](../../../reference/models/idp/). + +There are two store implementations provided by Duende IdentityServer: + +* An in-memory store +* A store backed by a database (using [Entity Framework Core](../../../data/ef/)) + +You could also implement your own store based on the [`IIdentityProviderStore` interface](../../../reference/stores/idp-store/). + +The configuration data for the OIDC provider is used to assign the configuration on the ASP.NET +Core [OpenID Connect Options](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectoptions) class, +much like you would if you were to statically configure the options when using `AddOpenIdConnect()`. + +The [identity provider model documentation](../../../reference/models/idp) provides details for the model +properties and how they are mapped to the options. + +:::tip[Consider caching dynamic identity providers] +Like other configuration data in IdentityServer, by default the dynamic provider configuration is loaded from the store +on every request unless caching is enabled. +If you use a custom store, there is an [extension method to enable caching](../../../data/configuration#caching-configuration-data). +If you use the EF stores, there is a general helper [to enable caching for all configuration data](../../../data/ef#enabling-caching-for-configuration-store). +::: + +Here's an example of adding a dynamic provider to an IdentityServer instance using the in-memory store: + +```csharp title=Program.cs {6-15} +builder.Services + .AddIdentityServer(options => + { + // ... + }) + .AddInMemoryIdentityProviders(new [] + { + new OidcProvider + { + Scheme = "oidc", + DisplayName = "Sample provider", + Enabled = true, + // ... more properties + } + }) +``` + +The identity provider store only provides an interface to query dynamic providers and does not provide any methods to add, update, or delete identity providers. +For custom store implementations, this means you'll need to implement a mechanism for populating the store with identity providers. + +If you're using the Entity Provider Core stored from the `Duende.IdentityServer.EntityFramework.Storage` NuGet package, +you can use the `ConfigurationDbContext` database context directly to add, update or remove dynamic identity providers: + +```csharp title="SeedData.cs" +private static async Task SeedDynamicProviders(ConfigurationDbContext context) +{ + if (!context.IdentityProviders.Any()) + { + Console.WriteLine("IdentityProviders being populated..."); + + context.IdentityProviders.Add(new OidcProvider + { + Scheme = "demoidsrv", + DisplayName = "IdentityServer (dynamic)", + Authority = "https://demo.duendesoftware.com", + ClientId = "login", + // ... more properties + }.ToEntity()); + + await context.SaveChangesAsync(); + + Console.WriteLine("IdentityProviders populated."); + } + else + { + Console.WriteLine("OidcIdentityProviders already populated"); + } +} +``` + +You can use the `ConfigurationDbContext` database context to add dynamic identity providers at runtime. + +## Listing Dynamic Providers On The Login Page + +When working with dynamic providers, you'll typically want to display a list of the available providers on the login +page. The [identity provider store (`IIdentityProviderStore`)](../../../reference/stores/idp-store/) can be used to query the database containing the dynamic providers. -```cs +```cs title="IIdentityProviderStore" {9} /// /// Interface to model storage of identity providers. /// @@ -47,19 +129,14 @@ public interface IIdentityProviderStore } ``` -These results can then be used to populate the list of options presented to the user on the login page. - -This API is deliberately separate than the `IAuthenticationSchemeProvider` provided by ASP.NET Core, which returns the -list of statically configured providers (from `Startup.cs`). -This allows the developer to have more control over the customization on the login page (e.g. there might be hundreds or -thousands on dynamic providers, and therefore you would not want them displayed on the login page, but you might have a -few social providers statically configured that you would want to display). +The `GetAllSchemeNamesAsync()` API returns a list of `IdentityProviderName` objects, which contain the scheme name and +display name of the provider and can be used on the login page, or in other places where you need this information. -Here is an example of how -the [IdentityServer Quickstart UI](https://github.com/DuendeSoftware/products/tree/main/identity-server/templates/src/UI/Pages/Account/Login/Index.cshtml.cs#l193-l210) -uses both interfaces to then present a merged and unified list to the end user: +In the [IdentityServer Quickstart UI](https://github.com/DuendeSoftware/products/tree/main/identity-server/templates/src/UI/Pages/Account/Login/Index.cshtml.cs#l193-l210), +dynamically registered identity providers will be automatically added to the list of providers on the login page by querying the identity provider store. +In custom UI implementations, you can use a similar approach to build and present a unified list of authentication providers to the end user: -```cs +```cs title="Login.cshtml.cs" var schemes = await _schemeProvider.GetAllSchemesAsync(); var providers = schemes @@ -81,40 +158,65 @@ var dynamicSchemes = (await _identityProviderStore.GetAllSchemeNamesAsync()) providers.AddRange(dynamicSchemes); ``` -### Store And Configuration Data - -To use the dynamic providers feature an [identity provider store](/identityserver/reference/stores/idp-store/) must be -provided that will load [model data](/identityserver/reference/models/idp/) for the OIDC identity provider to be used. -If you're using the [Entity Framework Integration](/identityserver/data/ef/) then this is implemented for you. +The above code will query the identity provider store for all statically registered authentication schemes and merge them with (enabled) dynamic providers. :::note -Like other configuration data in IdentityServer, by default the dynamic provider configuration is loaded from the store -on every request unless caching is enabled. -If you use a custom store, there is -an [extension method to enable caching](/identityserver/data/configuration#caching-configuration-data). -If you use the EF stores, there is general -helper [to enable caching for all configuration data](/identityserver/data/ef#enabling-caching-for-configuration-store). +The dynamic identity provider store API is deliberately separate from the `IAuthenticationSchemeProvider` provided by ASP.NET Core, which returns the +list of statically configured providers (from `Startup.cs`). + +This split allows the developer to have more control over the customization on the login page. For example, there might be hundreds or +thousands on dynamic providers, and therefore you would not want them displayed on the login page. At the same time, you might have a +few social providers statically configured that you would want to display. ::: -The configuration data for the OIDC provider is used to assign the configuration on the ASP.NET -Core [OpenID Connect Options](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectoptions) -class, much like you would if you were to statically configure the options when using `AddOpenIdConnect()`. -The [identity provider model documentation](/identityserver/reference/models/idp) provides details for the model -properties and how they are mapped to the options. +## Callback Paths + +As part of the architecture of the dynamic providers feature, different callback paths are required and are +automatically set to follow a convention. The convention of these paths follows the form of `~/federation/{scheme}/{suffix}`. + +There are three paths that are set on the `OpenIdConnectOptions`: + +* [CallbackPath](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.remoteauthenticationoptions.callbackpath). + This is the OIDC redirect URI protocol value. The suffix `"/signin"` is used for this path. +* [SignedOutCallbackPath](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectoptions.signedoutcallbackpath). + This is the OIDC post logout redirect URI protocol value. The suffix `"/signout-callback"` is used for this path. +* [RemoteSignOutPath](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectoptions.remotesignoutpath). + This is the OIDC front channel logout URI protocol value. The suffix `"/signout"` is used for this path. + +For your IdentityServer running at "https://sample.duendesoftware.com" and an OIDC identity provider whose +scheme is "idp1", your client configuration with the external OIDC identity provider would be: -#### Customizing OpenIdConnectOptions +* The redirect URI would be "https://sample.duendesoftware.com/federation/idp1/signin" +* The post logout redirect URI would be "https://sample.duendesoftware.com/federation/idp1/signout-callback" +* The front channel logout URI would be "https://sample.duendesoftware.com/federation/idp1/signout" -If it is needed to further customize the `OpenIdConnectOptions`, you can register in the DI system an instance of -`IConfigureNamedOptions`. For example: -```cs +## Advanced Configuration + +Dynamic identity providers in Duende IdentityServer come with a number of defaults and expose configuration options +that make sense in most scenarios. In this section, we'll cover some of the more advanced configuration options. + +### Customizing OpenIdConnectOptions + +While dynamic providers come with various configuration options, these are not as rich as the options available when +statically configuring authentication handlers using `AddOpenIdConnect()`. + +If you need to further customize the `OpenIdConnectOptions` for a particular provider, you can do so using a custom +`IConfigureNamedOptions` implementation. In the `Configure(string, OpenIdConnectOptions)` method, +you can override the `OpenIdConnectOptions` for the provider by name. + +```cs title="CustomConfig.cs" public class CustomConfig : IConfigureNamedOptions { public void Configure(string name, OpenIdConnectOptions options) { if (name == "MyScheme") { - options.ClaimActions.MapAll(); + // ... configure options + } + else if (name == "OtherScheme") + { + // ... configure options } } @@ -124,21 +226,33 @@ public class CustomConfig : IConfigureNamedOptions } ``` -And to register this in the DI system: +You will need to register the named options type in the service container at startup: -```cs +```csharp title="Program.cs" builder.Services.ConfigureOptions(); ``` -#### Accessing OidcProvider Data In IConfigureNamedOptions +:::note +In your `IConfigureNamedOptions`, you can use constructor injection to access other services. +For example, if you need to access your Entity Framework Core database context to retrieve additional data for a given +(dynamic) authentication scheme, you can do so if needed. + +If you require data from the `IIdentityProvider` store, you can use the `ConfigureAuthenticationOptions<>` base class +to further customize your dynamic identity provider, as we'll see in the next section. +::: + +### Accessing OidcProvider Data In IConfigureNamedOptions + +If your customization of `OpenIdConnectOptions` requires per-provider data that is available in the `IIdentityProvider` +and is accessible through properties of the `OidcProvider`, Duende IdentityServer provides an abstraction +for `IConfigureNamedOptions`. -If your customization of the `OpenIdConnectOptions` requires per-provider data that you are storing on the -`OidcProvider`, then we provide an abstraction for the `IConfigureNamedOptions`. This abstraction requires your code to derive from `ConfigureAuthenticationOptions` (rather than `IConfigureNamedOptions`). -For example: -```cs +Here's an example implementation: + +```cs title="CustomOidcConfigureOptions.cs" class CustomOidcConfigureOptions : ConfigureAuthenticationOptions { public CustomOidcConfigureOptions(IHttpContextAccessor httpContextAccessor, @@ -156,37 +270,169 @@ class CustomOidcConfigureOptions : ConfigureAuthenticationOptions(); ``` -### Callback Paths +### DynamicProviderOptions -As part of the architecture of the dynamic providers feature, the various callback paths are required and are -automatically set to follow a convention. -The convention of these paths follows the form of `~/federation/{scheme}/{suffix}`. +The `DynamicProviderOptions` is an options class in the IdentityServer options object model, and provides +[shared configuration options](../../../reference/options#dynamic-providers) for the dynamic identity providers +feature. For example, you can customize the path prefix for the dynamic providers callback path: -These are three paths that are set on the `OpenIdConnectOptions`: +```csharp title="Program.cs" +builder.Services + .AddIdentityServer(options => + { + // ... + + options.DynamicProviders.PathPrefix = "/fed"; + + // ... + }) +``` -* [CallbackPath](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.remoteauthenticationoptions.callbackpath). - This is the OIDC redirect URI protocol value. The suffix "/signin" is used for this path. -* [SignedOutCallbackPath](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectoptions.signedoutcallbackpath). - This is the OIDC post logout redirect URI protocol value. The suffix "/signout-callback" is used for this path. -* [RemoteSignOutPath](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectoptions.remotesignoutpath). - This is the OIDC front channel logout URI protocol value. The suffix "/signout" is used for this path. +## Using Non-OIDC Authentication Handlers -This means for your IdentityServer running at "https://sample.duendesoftware.com" and an OIDC identity provider whose -scheme is "idp1", your client configuration with the external OIDC identity provider would be: +Dynamic identity providers in Duende IdentityServer come with an implementation that supports OpenId Connect providers to be registered. +In your solution, it may be necessary to support other authentication providers, such as the `GoogleHandler`, or a SAML-based authentication provider. -* The redirect URI would be "https://sample.duendesoftware.com/federation/idp1/signin" -* The post logout redirect URI would be "https://sample.duendesoftware.com/federation/idp1/signout-callback" -* The front channel logout URI would be "https://sample.duendesoftware.com/federation/idp1/signout" +To register other authentication handlers, you can use the `AddProviderType(string scheme)` method on the `DynamicProviderOptions` object, +where `T` is the authentication handler type, `TOptions` is the options type for that particular handler, and `TIdentityProvider` is the identity provider type that models the dynamic provider. -### DynamicProviderOptions +The authentication handler type and options type will typically be provided by the authentication provider itself. +For example, the `GoogleHandler` and `GoogleOptions` types are provided by the `Google.AspNetCore.Authentication.OAuth` NuGet package. +`TIdentityProvider` will typically be a model class that maps to the identity provider data in the database +and can either be IdentityServer's [`IdentityProvider`](../../../reference/models/idp) class, or a custom type provided and implemented by you. + +Let's add Google authentication support to dynamic identity providers in IdentityServer! + +We'll assume you have already added the `Microsoft.AspNetCore.Authentication.Google` NuGet package to your project. + +### 1. Implement A Custom IdentityProvider Type + +While IdentityServer's [`IdentityProvider`](../../../reference/models/idp) class has a `Properties` bag that can be used +to store dynamic identity provider configuration data, it's recommended to use a custom type that is specific to the dynamic +identity provider. + +The `GoogleIdentityProvider` class can extend IdentityServer's [`IdentityProvider`](../../../reference/models/idp) class, +and expose additional properties that are specific to the Google identity provider. For a minimal Google implementation, +that would be the `ClientId` and `ClientSecret`: + +```csharp title="GoogleIdentityProvider.cs" +public class GoogleIdentityProvider : IdentityProvider +{ + public const string ProviderType = "google"; + + public GoogleIdentityProvider() + : base(ProviderType) + { + } + + public string? ClientId + { + get => this["ClientId"]; + set => this["ClientId"] = value; + } + + public string? ClientSecret + { + get => this["ClientSecret"]; + set => this["ClientSecret"] = value; + } +} +``` + +### 2. Register Dynamic Identity Provider Type + +In the host startup, you can register the handler and identity provider type. This registration provides IdentityServer +with a way to map the dynamic identity provider configuration type created in the previous step, to an authentication handler type +in ASP.NET Core. + +```csharp title="Program.cs" {6} +builder.Services + .AddIdentityServer(options => + { + // ... + + options.DynamicProviders + .AddProviderType( + GoogleIdentityProvider.ProviderType); + }) +``` + +### 3. Configure Authentication Handler Options + +With the dynamic identity provider type mapped to an ASP.NET Core authentication handler type, you'll need to make sure +an instance of the ASP.NET Core authentication handler options can be created based on a particular dynamic provider configuration. + +To do so, you can use the `ConfigureAuthenticationOptions` base class. In our Google example: + +```csharp title="GoogleDynamicConfigureOptions.cs" +class GoogleDynamicConfigureOptions + : ConfigureAuthenticationOptions +{ + public GoogleDynamicConfigureOptions(IHttpContextAccessor httpContextAccessor, + ILogger logger) : base(httpContextAccessor, logger) + { + } + + protected override void Configure( + ConfigureAuthenticationContext context) + { + var googleProvider = context.IdentityProvider; + var googleOptions = context.AuthenticationOptions; + + googleOptions.ClientId = googleProvider.ClientId; + googleOptions.ClientSecret = googleProvider.ClientSecret; + googleOptions.ClaimActions.MapAll(); + + googleOptions.SignInScheme = context.DynamicProviderOptions.SignInScheme; + googleOptions.CallbackPath = context.PathPrefix + "/signin"; + } +} +``` + +You will need to register this type in the service container at startup: -The `DynamicProviderOptions` is a new options class in the IdentityServer options object model. -It provides [shared settings](/identityserver/reference/options#dynamic-providers) for the dynamic identity providers -feature. +```csharp title="Program.cs" +builder.Services.ConfigureOptions(); +``` + +Note that for the `GoogleHandler` to work, you'll also need to register its `OAuthPostConfigureOptions<>` +to make sure data protection and state data formatters are registered. While this is an implementation detail of the +Google authentication handler, a similar implementation detail may exist for the custom dynamic provider type you are building. + +```csharp title="Program.cs" +builder.Services.ConfigureOptions>(); +``` + +### 4. Use Your Custom IdentityProvider + +With these building blocks in place, you can start using your custom identity provider type with Duende IdentityServer +dynamic identity providers! + +```csharp title="Program.cs" +builder.Services + .AddIdentityServer(options => + { + // ... + + options.DynamicProviders + .AddProviderType( + GoogleIdentityProvider.ProviderType); + }) + .AddInMemoryIdentityProviders(new [] + { + new GoogleIdentityProvider + { + Scheme = "google1", + DisplayName = "Google 1", + Enabled = true, + ClientId = "...", + ClientSecret = "..." + } + }) +``` \ No newline at end of file From 4d1fb5556fd0c9e97b00fa4b13b07837ea32ed6b Mon Sep 17 00:00:00 2001 From: Maarten Balliauw Date: Fri, 18 Apr 2025 07:35:00 +0200 Subject: [PATCH 2/5] Fix PR comment --- .../docs/identityserver/ui/login/dynamicproviders.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/content/docs/identityserver/ui/login/dynamicproviders.md b/src/content/docs/identityserver/ui/login/dynamicproviders.md index 8d189d98e..55ac7745d 100644 --- a/src/content/docs/identityserver/ui/login/dynamicproviders.md +++ b/src/content/docs/identityserver/ui/login/dynamicproviders.md @@ -234,11 +234,13 @@ builder.Services.ConfigureOptions(); :::note In your `IConfigureNamedOptions`, you can use constructor injection to access other services. -For example, if you need to access your Entity Framework Core database context to retrieve additional data for a given -(dynamic) authentication scheme, you can do so if needed. +Beware of performance, for example when accessing your Entity Framework Core database context here. +We recommend caching any additional data you may need as part of configuring options. -If you require data from the `IIdentityProvider` store, you can use the `ConfigureAuthenticationOptions<>` base class -to further customize your dynamic identity provider, as we'll see in the next section. +Note that the `OidcProvider` class has a `Properties` bag that can be used to store additional dynamic identity provider +configuration data you could use to further customize the `OpenIdConnectOptions`. When you require this (or other) data +from the `IIdentityProvider` store, you can use the `ConfigureAuthenticationOptions<>` base class to further customize +your dynamic identity provider, as we'll see in the next section. ::: ### Accessing OidcProvider Data In IConfigureNamedOptions From 1f259d805436742db8b9735c066123e555afe90d Mon Sep 17 00:00:00 2001 From: Maarten Balliauw Date: Fri, 18 Apr 2025 07:40:32 +0200 Subject: [PATCH 3/5] Fix PR comment --- .../identityserver/ui/login/dynamicproviders.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/content/docs/identityserver/ui/login/dynamicproviders.md b/src/content/docs/identityserver/ui/login/dynamicproviders.md index 55ac7745d..cf242ef28 100644 --- a/src/content/docs/identityserver/ui/login/dynamicproviders.md +++ b/src/content/docs/identityserver/ui/login/dynamicproviders.md @@ -233,14 +233,14 @@ builder.Services.ConfigureOptions(); ``` :::note -In your `IConfigureNamedOptions`, you can use constructor injection to access other services. -Beware of performance, for example when accessing your Entity Framework Core database context here. -We recommend caching any additional data you may need as part of configuring options. - -Note that the `OidcProvider` class has a `Properties` bag that can be used to store additional dynamic identity provider -configuration data you could use to further customize the `OpenIdConnectOptions`. When you require this (or other) data -from the `IIdentityProvider` store, you can use the `ConfigureAuthenticationOptions<>` base class to further customize -your dynamic identity provider, as we'll see in the next section. +While you can use constructor injection in your `IConfigureNamedOptions` to access other services, +we recommend using that technique sparingly for performance reasons. If you do need to access your Entity Framework Core +database context here, make sure to cache any additional data you may need as part of configuring options. + +Alternatively, the `OidcProvider` class has a `Properties` bag that can be used to store additional dynamic identity provider +configuration data. You can use this data to further customize the `OpenIdConnectOptions`, instead of making additional database +calls. When you require access to the `Properties` bag or the `IIdentityProvider` store, you can use the `ConfigureAuthenticationOptions<>` +base class to further customize your dynamic identity provider, as we'll see in the next section. ::: ### Accessing OidcProvider Data In IConfigureNamedOptions From 820c1aab6de6432a544f82c5ae9e276099f5dede Mon Sep 17 00:00:00 2001 From: khalidabuhakmeh Date: Fri, 18 Apr 2025 16:19:10 -0400 Subject: [PATCH 4/5] Refine documentation for clarity and consistency Updated links, clarified terminology, and improved formatting for better readability. Adjusted code and text references to align with standard conventions and enhance user understanding. --- .../endpoints/dynamic-registration.md | 3 ++- .../identityserver/ui/login/dynamicproviders.md | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/content/docs/identitymodel/endpoints/dynamic-registration.md b/src/content/docs/identitymodel/endpoints/dynamic-registration.md index 690f7aad5..21b352182 100644 --- a/src/content/docs/identitymodel/endpoints/dynamic-registration.md +++ b/src/content/docs/identitymodel/endpoints/dynamic-registration.md @@ -9,7 +9,8 @@ redirect_from: --- The client library for [OpenID Connect Dynamic Client Registration](https://openid.net/specs/openid-connect-registration-1_0.html) -is provided as an extension method for *HttpClient*. +is provided as an extension method for [ +`System.Net.Http.HttpClient`](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient). The following code sends a registration request: diff --git a/src/content/docs/identityserver/ui/login/dynamicproviders.md b/src/content/docs/identityserver/ui/login/dynamicproviders.md index cf242ef28..a51a6347c 100644 --- a/src/content/docs/identityserver/ui/login/dynamicproviders.md +++ b/src/content/docs/identityserver/ui/login/dynamicproviders.md @@ -10,7 +10,7 @@ redirect_from: --- Dynamic Identity Providers are a scalable solution for managing authentication with lots of external providers, without -incurring performance penalties or requiring application recompilation. This feature, included in the Enterprise Edition +incurring performance penalties or requiring application recompilation. This feature, included in the [Enterprise Edition](/general/licensing/#enterprise-edition) of Duende IdentityServer, enables providers to be configured dynamically from a store at runtime. ## Dynamic Identity Providers @@ -76,7 +76,7 @@ builder.Services The identity provider store only provides an interface to query dynamic providers and does not provide any methods to add, update, or delete identity providers. For custom store implementations, this means you'll need to implement a mechanism for populating the store with identity providers. -If you're using the Entity Provider Core stored from the `Duende.IdentityServer.EntityFramework.Storage` NuGet package, +If you're using the Entity Framework Core identity provider store from the `Duende.IdentityServer.EntityFramework.Storage` NuGet package, you can use the `ConfigurationDbContext` database context directly to add, update or remove dynamic identity providers: ```csharp title="SeedData.cs" @@ -183,12 +183,12 @@ There are three paths that are set on the `OpenIdConnectOptions`: * [RemoteSignOutPath](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectoptions.remotesignoutpath). This is the OIDC front channel logout URI protocol value. The suffix `"/signout"` is used for this path. -For your IdentityServer running at "https://sample.duendesoftware.com" and an OIDC identity provider whose +For your IdentityServer running at `https://sample.duendesoftware.com` and an OIDC identity provider whose scheme is "idp1", your client configuration with the external OIDC identity provider would be: -* The redirect URI would be "https://sample.duendesoftware.com/federation/idp1/signin" -* The post logout redirect URI would be "https://sample.duendesoftware.com/federation/idp1/signout-callback" -* The front channel logout URI would be "https://sample.duendesoftware.com/federation/idp1/signout" +* The redirect URI would be `https://sample.duendesoftware.com/federation/idp1/signin` +* The post logout redirect URI would be `https://sample.duendesoftware.com/federation/idp1/signout-callback` +* The front channel logout URI would be `https://sample.duendesoftware.com/federation/idp1/signout` ## Advanced Configuration @@ -411,9 +411,9 @@ Google authentication handler, a similar implementation detail may exist for the builder.Services.ConfigureOptions>(); ``` -### 4. Use Your Custom IdentityProvider +### 4. Use A Custom IdentityProvider -With these building blocks in place, you can start using your custom identity provider type with Duende IdentityServer +With these building blocks in place, you can start using a custom identity provider type with Duende IdentityServer dynamic identity providers! ```csharp title="Program.cs" From 71022c07f90efe5eaff4edb32a0d073956359bc8 Mon Sep 17 00:00:00 2001 From: Maarten Balliauw Date: Tue, 22 Apr 2025 21:27:19 +0200 Subject: [PATCH 5/5] PR feedback --- .../ui/login/dynamicproviders.md | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/content/docs/identityserver/ui/login/dynamicproviders.md b/src/content/docs/identityserver/ui/login/dynamicproviders.md index 6e5b957f4..0a008127c 100644 --- a/src/content/docs/identityserver/ui/login/dynamicproviders.md +++ b/src/content/docs/identityserver/ui/login/dynamicproviders.md @@ -15,15 +15,16 @@ of Duende IdentityServer, enables providers to be configured dynamically from a ## Dynamic Identity Providers -Authentication handlers for external providers are typically added into your IdentityServer using `AddAuthentication()` -and `AddOpenIdConnect()`. This is fine for a handful of schemes, but becomes harder to manage if you have too many of them. +Authentication handlers for external providers are typically added into your IdentityServer using `AddAuthentication()`, +`AddOpenIdConnect()`, `AddSaml2()`, and other helper methods. This is fine for a handful of schemes, but becomes harder +to manage if you have too many of them. Additionally, you'd have to re-run your startup code for new authentication handlers to be picked up by ASP.NET Core. The authentication handler architecture in ASP.NET Core was not designed to have many statically registered authentication handlers registered in the service container and Dependency Injection (DI) system. At some point you will incur a performance penalty for having too many of them. -Duende IdentityServer provides support for dynamic configuration of OpenID Connect providers loaded from a store. +Duende IdentityServer provides support for dynamic configuration of authentication handlers loaded from a store. Dynamic configuration addresses the performance concern and allows changes to the configuration to a running server. Support for Dynamic Identity Providers is included in the [Duende IdentityServer](https://duendesoftware.com/products/identityserver) Enterprise Edition. @@ -174,6 +175,11 @@ few social providers statically configured that you would want to display. As part of the architecture of the dynamic providers feature, different callback paths are required and are automatically set to follow a convention. The convention of these paths follows the form of `~/federation/{scheme}/{suffix}`. +:::tip +Even if you don't use dynamic providers yet, you may want to consider adopting this pattern for the callback paths. +This will make it easier to transition to dynamic providers in the future. +::: + There are three paths that are set on the `OpenIdConnectOptions`: * [CallbackPath](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.remoteauthenticationoptions.callbackpath). @@ -298,7 +304,13 @@ builder.Services ## Using Non-OIDC Authentication Handlers Dynamic identity providers in Duende IdentityServer come with an implementation that supports OpenId Connect providers to be registered. -In your solution, it may be necessary to support other authentication providers, such as the `GoogleHandler`, or a SAML-based authentication provider. +In your solution, it may be necessary to support other authentication providers, such as a SAML-based authentication provider. + +We have two samples that show how to use non-OIDC authentication handlers with dynamic identity providers: +* Adding the [WS-Federation protocol type](../../../identityserver/samples/ui/#adding-other-protocol-types-to-dynamic-providers) +* Adding the [Saml2 protocol type](../../../identityserver/samples/ui/#using-sustainsyssaml2-with-dynamic-providers), using the [Sustainsys.Saml2](https://saml2.sustainsys.com/) open source library + +In this section, we'll look at a minimal example of how to add other authentication handlers, such as the `GoogleHandler`, to dynamic identity providers, To register other authentication handlers, you can use the `AddProviderType(string scheme)` method on the `DynamicProviderOptions` object, where `T` is the authentication handler type, `TOptions` is the options type for that particular handler, and `TIdentityProvider` is the identity provider type that models the dynamic provider.