This post looks at customizing the sign-in UI and the sign-in options in an ASP.NET Core application using Duende IdentityServer and ASP.NET Core Identity. There are multiple ways of changing the look and feel of the UI for different OpenID Connect clients or different client flows. In the previous post, the UI was customized per client, this post customizes inside a single client.
Code: https://github.com/damienbod/duende-multi-tenant
Blogs in the series
- Multiple client sign-in customizations using Duende identity provider
- Customizing a single client sign-in using parameters in Duende IdentityServer
Setup
The solution is setup using three different ASP.NET Core applications. In the example code, the “admin” application has different federation authentication options compared to the “shop” client authentication sign-in experience. The client ID from the authentication context is used to customize the look and feel, i.e. the styles, the layout and the options of the client are used to define which federation and authentication options are possible. The shop client can be further customized using authentication parameters sent in the OpenID Connect redirect.

OIDC client implementation
In ASP.NET Core the OpenID Connect flow implementation provides multiple events which can be changed or extended. The OnRedirectToIdentityProvider can be used to send custom parameters to the OpenID Connect server. The OAuth Pushed authorization request on top of OpenID Connect Core is used used per default in .NET 9, if the OpenID Connect server supports this.
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// other options ...
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("showadminsignin", "false");
return Task.FromResult(0);
}
};
});
Duende sign-in customization
In the previous post, a new sign-in UI was created for each client. The shop client has further customization. In this demo, the admin external provider can be hidden or displayed depending on what the client requests. The UI is implemented using ASP.NET Core Razor pages and a BindProperty is used for this.
[BindProperty]
public bool ShowAdminSignIn { get; set; } = true;
When processes the authentication using Duende and ASP.NET Core Identity, the GetAuthorizationContextAsync method can be used to get the client requested parameters.
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
ShowAdminSignIn = !(context?.Parameters["showadminsignin"] == "false");
The UI can implement any display logic using the client parameters sent in the request. In this demo, the admin UI is hidden or displayed using the client request data.
@foreach (var provider in Model.View.VisibleExternalProviders)
{
if (!(!Model.ShowAdminSignIn &&
(provider.AuthenticationScheme == "AdminEntraID")))
{
<li class="list-inline-item">
<a class="btn btn-secondary"
asp-page="/ExternalLogin/Challenge"
asp-route-scheme="@provider.AuthenticationScheme"
asp-route-returnUrl="@Model.Input.ReturnUrl">
@provider.DisplayName
</a>
</li>
}
}
Notes
This is an easy approach to implement UI customization on a per client basis. The is UI logic and not authorization. The different options are just hidden or removed from the UI using the client parameters, the logic MUST not work if this is a security requirement. This is not authorization.
Links
https://docs.duendesoftware.com/identityserver/v7
https://docs.duendesoftware.com/identityserver/v7/ui/federation/