Forums, Documentation & Knowledge Base - ComponentSpace

Integrate SAML with IdentityServer4 dynamically


https://componentspace.com/forums/Topic10517.aspx

By [email protected] - 1/7/2020

Hello there,

The scenario here is, we have centralized a IdentityServer4 that will act as service provider and there are multiple identity providers like Active Directory, Google, Facebook and also other SAML providers based on each tenant. i.e., one service provider and multiple identity providers.

To load the openId configs from database I am exactly following the https://stackoverflow.com/a/56941908/2922388 and it is working as expected for openid and now I need to integrate SAML providers in the same way.

I went through the  "SAMLv20.Core-evaluation" and was able to do the integration using appsettings.json successfully. But I am not sure how to integrate it programatically as given in https://stackoverflow.com/a/56941908/2922388.

Here is what I have done so far

public class AccountController : ControllerBase
{
private readonly IOptionsMonitorCache<OpenIdConnectOptions> _openIdOptionsCache;
private readonly IOptionsMonitorCache<SamlAuthenticationOptions> _samlOptionsCache;
private readonly OpenIdConnectPostConfigureOptions _postConfigureOptions;
private readonly SamlPostConfigureAuthenticationOptions _samlPostConfigureOptions;

public AccountController(
IOptionsMonitorCache<OpenIdConnectOptions> openidOptionsCache,
IOptionsMonitorCache<SamlAuthenticationOptions> samlOptionsCache,
OpenIdConnectPostConfigureOptions postConfigureOptions,
SamlPostConfigureAuthenticationOptions samlPostConfigureOptions
)
{
_openIdOptionsCache = openidOptionsCache;
_samlOptionsCache = samlOptionsCache;
_postConfigureOptions = postConfigureOptions;
_samlPostConfigureOptions = samlPostConfigureOptions;
}

private async Task<IEnumerable<AuthenticationScheme>> LoadAuthenticationSchemesByTenant(IEnumerable<AuthenticationScheme> schemes, AuthProviderSetting tenantAuthProviderSetting)
{
dynamic configJson = JsonConvert.DeserializeObject(tenantAuthProviderSetting.tenantConfigJson);
switch (tenantAuthProviderSetting.AuthenticationType)
{
case AuthenticationTypes.OpenID:
var oidcOptions = new OpenIdConnectOptions
{
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
SignOutScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
SaveTokens = true,
Authority = configJson.Authority,
ClientId = configJson.ClientId,
ClientSecret = configJson.ClientSecret,

TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role",
ValidateIssuer = false
}
};
_schemeProvider.AddScheme(new AuthenticationScheme(tenantAuthProviderSetting.AuthenticationScheme, tenantAuthProviderSetting.DisplayName, typeof(OpenIdConnectHandler)));
_postConfigureOptions.PostConfigure(tenantAuthProviderSetting.AuthenticationScheme, oidcOptions);
_openIdOptionsCache.TryAdd(tenantAuthProviderSetting.AuthenticationScheme, oidcOptions);
schemes = await _schemeProvider.GetAllSchemesAsync();
break;

case AuthenticationTypes.SAML:
var samlOptions = new SamlAuthenticationOptions
{

PartnerName = delegate () { return "https://ExampleIdentityProvider"; },
SingleLogoutServicePath = "https://localhost:44313/SAML/SingleLogoutService",

// Not sure how to set other parameters here

};

_schemeProvider.AddScheme(new AuthenticationScheme(tenantAuthProviderSetting.AuthenticationScheme, tenantAuthProviderSetting.DisplayName, typeof(SamlAuthenticationHandler)));
_samlPostConfigureOptions.PostConfigure(tenantAuthProviderSetting.AuthenticationScheme, samlOptions);
_samlOptionsCache.TryAdd(tenantAuthProviderSetting.AuthenticationScheme, samlOptions);
schemes = await _schemeProvider.GetAllSchemesAsync();
break;
default:
schemes = await _schemeProvider.GetAllSchemesAsync();
break;

}
return schemes;
}
}

Here is the config through which I used to statically integrate with IdentityServer

"SAML": {
  "$schema": "https://www.componentspace.com/schemas/saml-config-schema-v1.0.json",
  "Configurations": [
   {
    "LocalServiceProviderConfiguration": {
    "Name": "https://IdentityServer4",
    "Description": "IdentityServer4",
    "AssertionConsumerServiceUrl": "http://localhost:44380/SAML/AssertionConsumerService",
    "SingleLogoutServiceUrl": "http://localhost:44380/SAML/SingleLogoutService",
    "LocalCertificates": [
     {
      "FileName": "certificates/sp.pfx",
      "Password": "password"
     }
    ]
    },
    "PartnerIdentityProviderConfigurations": [
    {
     "Name": "https://ExampleIdentityProvider",
     "Description": "Example Identity Provider",
     "SignAuthnRequest": true,
     "SingleSignOnServiceUrl": "https://localhost:44313/SAML/SingleSignOnService",
     "SingleLogoutServiceUrl": "https://localhost:44313/SAML/SingleLogoutService",
     "PartnerCertificates": [
      {
      "FileName": "certificates/idp.cer"
      }
     ]
    }
    ]
   }
  ]
  },
  "PartnerName": "https://ExampleIdentityProvider"

I've also raised the same in stackoverflow (https://stackoverflow.com/questions/59646417/load-dynamic-saml-schemes-for-identityserver4-using-componentspace) as well.


By ComponentSpace - 1/12/2020

That's not possible. The SAML authentication handler follows the same pattern set out by Microsoft for all authentication handlers. The AuthenticationBuilder.AddScheme method only supports specifying the display name as a string. This occurs when the authentication scheme and associated authentication handler are registered.

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationbuilder.addscheme?view=aspnetcore-3.1