IdP initiated logon never calling GetPartnerIdentityProviderConfigurationAsync

We have a multi-tenant setup where we store SAML config data in a local database. When using local service provider login, it works great, for example the flow is:

  1. Enter their email address into out platform
  2. That checks if they are using SSO and sets the config accordingly using await _samlServiceProvider.SetConfigurationIDAsync(email) which calls the method GetLocalServiceProviderConfigurationAsync from within our CustomConfigurationResolver class- all well and good.
  3. That happily redirects to, for this example. Azure login page where they login
  4. Microsoft then redirect back to our AssertionConsumerService URL and calls our OnGetCallbackAsync in the ExternalLogin class
  5. We then decide what to do with this user after using ConfigurationId (email address in our case) to check their account etc - all working great!
But when we test IdP initiated logon from, for example Microsoft https://myapps.microsoft.com/, this falls down with the process looking like this:
  1. After the “App” is selected from the Microsoft portal, it asks for appropriate authentication, all good so far
  2. MS then calls our application’s GetLocalServiceProviderConfigurationAsync again but this time with a null in the ConfigurationId (email in our case), when I would expect (although this is where my knowledge is breaking down) it to call GetPartnerIdentityProviderConfigurationAsync with the signature ConfigurationId (email in our case) and partner name, but this method never gets called.

Our Startup class has:

services.AddSaml();
services.AddTransient<ISamlConfigurationResolver, Classes.Account.CustomConfigurationResolver>();
services.AddAuthentication()

Apologies if we have missed something obvious but any advice would be appreciated.

Thanks.
Jim.


UPDATE
Within the CustomConfigurationResolver class when the GetLocalServiceProviderConfigurationAsync gets called first using IdP initiated and, because we are passed a null, we have no choice but to return an empty (not null) instance of LocalServiceProviderConfiguration. When doing so the GetPartnerIdentityProviderConfigurationAsync does actually then get called but an Id of null (don’t know why). The Partner name however does contain a value we can use but ideally we would also like the Id (email address in our case).

We then find their record and populate and return PartnerIdentityProviderConfiguration, but the ExternalLogin class is not called when we would next expect it to.

UPDATE 2
So when I hard code the LocalServiceProviderConfiguration in the GetLocalServiceProviderConfigurationAsync method in the CustomConfigurationResolver class it works fine and logs in perfectly using IdP, but this is impossible to populate whilst the ConfigurationId is null and I cannot obtain that information from the database.

UPDATE 3 - WORKING

Leaving post up in case it helps anyone else / or someone can correct our process.

It seems we HAVE to have something in the LocalServiceProviderConfiguration, but it only needs to be the Name and the AssertionConsumerServiceUrl properties. These are static across our multi-tenancy so if the ConfigurationId being passed in is null we simply create a new instance of LocalServiceProviderConfiguration and populate those two properties. With that done, all works = phew!

Hi Jim,

Thanks for your post including the various steps you tried.

In a multi-tenanted application, we require the configuration ID to be specified before processing any SSO or SLO flow. This includes both SP-initiated SSO and IdP-initiated SSO.

The configuration ID identifies both the local service provider configuration and, along with the partner name, the partner identity provider configuration to use to process the SSO/SLO flow.

For SP-initiated SSO, specifying the configuration ID is fairly straightforward as you’ve outlined.

For IdP-initiated SSO, it’s less so as the first interaction at the SP is the SAML response being received from the IdP. Please note that the IdP has no knowledge of the configuration ID as this is internal to the SP.

The approach you’ve taken is perfectly fine if the local service provider configuration is the same across each tenant. However, if that’s the case, you possibly don’t need the multi-tenancy support.

If the local service provider configuration is different for each tenant, the application is responsible for identifying the tenant and setting the configuration ID appropriately. Our Configuration Guide lists some suggested ways for identifying the tenant but in most cases it would involve including some identifying information in the URL.

To communicate the configuration ID to the SAML authentication handler, you should register a delegate using the SamlAuthenticationOptions.ConfigurationName property. The SAML authentication handler will call this delegate whenever it needs to set the configuration ID. For example, this would be immediately prior to processing the SAML response.