Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IdentityServer4 multiple WSFederation-providers cause an exception

I was told that the issue I will describe here is not a bug in the IdentityServer, so I'm probably doing something wrong:

This code works, using a single WSFederation-instance as identity provider in the QuickStart-project using EFCore.

Registering the provider:

services.AddAuthentication()
            .AddWsFederation("WsFederation", options =>
            {
                options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                options.Wtrealm = realm;
                options.MetadataAddress = metadata;
                options.Events.OnTicketReceived += OnTicketReceived;
            })

OnTicketReceived-Eventhandler:

/// <summary>
/// Transform the UPN-claim to the sub-claim to be compatible with IdentityServer4
/// </summary>
private async Task OnTicketReceived(TicketReceivedContext ticketReceivedContext)
{
     var identity = ticketReceivedContext.Principal.Identities.First();
     identity.AddClaim(new Claim("sub", ticketReceivedContext.Principal.FindFirstValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")));

}

As soon as I add a second provider, I will get an exception when trying to use the second provider, because the first provider added takes the request and throws an exception as soon as it receives the request:

services.AddAuthentication()
.AddWsFederation("WsFederation_LocalHost", "WsFederation_LocalHost", options =>
                {
                    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                    options.Wtrealm = "urn:aspnetcorerp";
                    options.MetadataAddress = "http://localhost:5000/wsfederation";
                    options.Events.OnTicketReceived += OnTicketReceived;
                    options.RequireHttpsMetadata = false;
                })
                .AddWsFederation("WsFederation_SVN", "WsFederation_SVN", options =>
                {
                    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                    options.Wtrealm = realm;
                    options.MetadataAddress = metadata;
                    options.Events.OnTicketReceived += OnTicketReceived;
                    options.Events.OnSecurityTokenReceived += OnSecurityTokenReceived;
                })

The exception I get is this - if I fix it by allowing unsolicited logins other exceptions will occure as it still tries to use the wrong provider:

System.Exception: Unsolicited logins are not allowed. at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.d__12.MoveNext()

I found the point where it is raised in IdentityServer4:

public async Task<bool> HandleRequestAsync()
    {
var result = await _inner.HandleRequestAsync();

I can fix this if I change the above code to catch the Exception, as it will afterwards pass the correct provider and the login succeeds:

var result = false;
try
{
   result = await _inner.HandleRequestAsync();
}
catch (Exception) {}

I am not happy with having to fork IdentityServer4 to resolve this problem, so I am asking for a solution without changing the code of the IdentityServer. The place where I could step in and change something is either the configuration of my WSFederation-endpoint, right before starting the ExternalLogin or at the callback in the AccountController.

Callback in the AccountController:

    [HttpGet]
    public async Task<IActionResult> ExternalLoginCallback()
    {
        // read external identity from the temporary cookie - I don't know how I could change which AuthenticationMiddleware gets called
        var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);

Any hints where I should start are greatly appreciated.

like image 683
Christoph Sonntag Avatar asked Jan 30 '23 01:01

Christoph Sonntag


1 Answers

Got it - the solution is to set different CallbackPaths for the different providers:

services.AddAuthentication()
                .AddWsFederation("WsFederation_LocalHost", "WsFederation_LocalHost", options =>
                {
                    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                    options.Wtrealm = "urn:aspnetcorerp";
                    options.MetadataAddress = "http://localhost:5000/wsfederation";
                    options.Events.OnTicketReceived += OnWsFedTicketReceived;
                    options.RequireHttpsMetadata = false;
                    options.CallbackPath = "/signin-wsfed-localhost";
                })
                .AddWsFederation("WsFederation_SVN", "WsFederation_SVN", options =>
                {
                    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                    options.Wtrealm = realm;
                    options.MetadataAddress = metadata;
                    options.Events.OnTicketReceived += OnWsFedTicketReceived;
                    options.CallbackPath = "/signin-wsfed-svn";
                })
like image 191
Christoph Sonntag Avatar answered Feb 06 '23 10:02

Christoph Sonntag