SAML Multi-Tenancy Applications

You’re welcome!

Hi, we use DB as configuration store and our problem ist that loading all service provider configuration at once is to slow. Users need to wait on coldstart some sec. We write admin backend to manager configuration on the fly.
What we need is to load service provider configuration by name e.g.:

configProvider.GetServiceProviderConfigurationByName(“http://localhost/ExampleServiceProvider2”)
or
configProvider.GetServiceProviderConfigurationByName(“http://localhost/ExampleServiceProvider2”, “tenantID1”)
for multi-tenancy applications.

We would cache the config loaded for some time to get more performance. This is would be much faster than loading all config at once or restart the whole application on config changes.
Is there an interface we can use to implement this behavier? Or any plans to write such abstraction provider.

Any further ideas?

Thank you in advance.

Best Regards

Alexej

Hi Alexej
Just to confirm, you would like to see an interface that allows you to control the loading of the SAML configuration and specifically so it can be loaded piecemeal and as required?
How many service provider configurations are you loading?
Is it possible to speed up the database access?


[quote]Just to confirm, you would like to see an interface that allows you to control the loading of the SAML configuration and specifically so it can be loaded piecemeal and as required?[/quote]
Correct.
[quote]How many service provider configurations are you loading?[/quote]
at the moment 11 but there are much more to come. (we use EntityFramework Core)
[quote]Is it possible to speed up the database access?[/quote]
I am working on it but, this would be still an issue if we load whole config at once.

Thanks for the information.
We’ll discuss this internally to see what’s the best way to provide this functionality.
Please email us mentioning this topic so we get your feedback of any proposals.

Sorry for bumping an old thread, but I found this in the search and it’s exactly what I’m attempting.

Currently, we use this code to authenticate within our AssertionConsumer page, but only allow one IDP at a time.

try
{
var samlConfigs = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations(“Web”);
var samlConfig = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations(“Web”).First();
var samlConfiguration = BuildSamlConfiguration(samlConfig, _siteNameResolver.SiteName);
// set the SAML configuration to the current one before we InitiateSSO
SAMLConfiguration.Current = samlConfiguration;

var isInResponseTo = false;
string partnerIdP;
SAMLServiceProvider.ReceiveSSO(request, out isInResponseTo, out partnerIdP, out userName, out attributes, out targetUrl);
}
catch (Exception)
{
rv.IsSuccess = false;
rv.ErrorMessage = “There was an error attempting to log in via the SAML single sign on gateway. Please contact support for further assistance.”;
return rv;
}


My question is if we went to a multi-tenant setup, what would we do to know what the user’s saml config the assertion is for? Or is that automatic?

[quote]
james.garrett - 1/11/2018
Sorry for bumping an old thread, but I found this in the search and it's exactly what I'm attempting.

Currently, we use this code to authenticate within our AssertionConsumer page, but only allow one IDP at a time.

try
{
var samlConfigs = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations("Web");
var samlConfig = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations("Web").First();
var samlConfiguration = BuildSamlConfiguration(samlConfig, _siteNameResolver.SiteName);
// set the SAML configuration to the current one before we InitiateSSO
SAMLConfiguration.Current = samlConfiguration;

var isInResponseTo = false;
string partnerIdP;
SAMLServiceProvider.ReceiveSSO(request, out isInResponseTo, out partnerIdP, out userName, out attributes, out targetUrl);
}
catch (Exception)
{
rv.IsSuccess = false;
rv.ErrorMessage = "There was an error attempting to log in via the SAML single sign on gateway. Please contact support for further assistance.";
return rv;
}


My question is if we went to a multi-tenant setup, what would we do to know what the user's saml config the assertion is for? Or is that automatic?

[/quote]

One way is when you're creating and giving your metadata away to put an identifier into the URLs. For example, in your ACS endpoint that the IdP configures, you can put a special code that identifies the config to use (http://acme.com/acs/123). Then, in the SamlController, your ACS endpoint would look like this:


public ActionResult ACS(string id)
{
// id parameter is the tenant
try
{
var samlConfigs = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations(id);
var samlConfig = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations(id).First();
var samlConfiguration = BuildSamlConfiguration(samlConfig, _siteNameResolver.SiteName);
// set the SAML configuration to the current one before we InitiateSSO
SAMLConfiguration.Current = samlConfiguration;
var isInResponseTo = false;
string partnerIdP;
SAMLServiceProvider.ReceiveSSO(request, out isInResponseTo, out partnerIdP, out userName, out attributes, out targetUrl);
}
catch (Exception)
{
rv.IsSuccess = false;
rv.ErrorMessage = "There was an error attempting to log in via the SAML single sign on gateway. Please contact support for further assistance.";
return rv;
}
}

Just as a note, the interface referred to earlier was introduced in v2.8.4.
It’s the ISAMLConfiguartionResolver under the ComponentSpace.SAML2.Configuration.Resolver namespace.

[quote]
james.garrett - 1/11/2018
Sorry for bumping an old thread, but I found this in the search and it's exactly what I'm attempting.

Currently, we use this code to authenticate within our AssertionConsumer page, but only allow one IDP at a time.

try
{
var samlConfigs = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations("Web");
var samlConfig = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations("Web").First();
var samlConfiguration = BuildSamlConfiguration(samlConfig, _siteNameResolver.SiteName);
// set the SAML configuration to the current one before we InitiateSSO
SAMLConfiguration.Current = samlConfiguration;

var isInResponseTo = false;
string partnerIdP;
SAMLServiceProvider.ReceiveSSO(request, out isInResponseTo, out partnerIdP, out userName, out attributes, out targetUrl);
}
catch (Exception)
{
rv.IsSuccess = false;
rv.ErrorMessage = "There was an error attempting to log in via the SAML single sign on gateway. Please contact support for further assistance.";
return rv;
}


My question is if we went to a multi-tenant setup, what would we do to know what the user's saml config the assertion is for? Or is that automatic?

[/quote]

One way is when you're creating and giving your metadata away to put an identifier into the URLs. For example, in your ACS endpoint that the IdP configures, you can put a special code that identifies the config to use (http://acme.com/acs/123). Then, in the SamlController, your ACS endpoint would look like this:


public ActionResult ACS(string id)
{
// id parameter is the tenant
try
{
var samlConfigs = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations(id);
var samlConfig = _samlConfigurationQueryFunctionality.GetActiveSamlConfigurations(id).First();
var samlConfiguration = BuildSamlConfiguration(samlConfig, _siteNameResolver.SiteName);
// set the SAML configuration to the current one before we InitiateSSO
SAMLConfiguration.Current = samlConfiguration;
var isInResponseTo = false;
string partnerIdP;
SAMLServiceProvider.ReceiveSSO(request, out isInResponseTo, out partnerIdP, out userName, out attributes, out targetUrl);
}
catch (Exception)
{
rv.IsSuccess = false;
rv.ErrorMessage = "There was an error attempting to log in via the SAML single sign on gateway. Please contact support for further assistance.";
return rv;
}
}

[/quote]
Thanks, I was hoping that the receive SSO method would be able to detect the entity id and I wouldn't need to do that. ComponentSpace, any feedback?

We don’t attempt to determine which SAML configuration should be used. This is the responsibility of the application.
In a multi-tenanted configuration there’s one SAML configuration per tenant. Each configuration includes a unique configuration ID.
Prior to any SAML SSO API call, you need to specify the correct configuration ID so we know which configuration to use when processing the SAML SSO.
This is done by setting the SAMLController.ConfigurationID property.
For example:
SAMLController.ConfigurationID = “tenant1”;
SAMLServiceProvider.ReceiveSSO(…);
This assumes that there’s a SAML configuration whose ID is “tenant1”.
Determining which configuration to use is the responsibility of the application.
If each tenant has a separate subdomain name, this can be used by the application as the tenant name (ie configuration ID).

[quote]
ComponentSpace - 1/11/2018
Just as a note, the interface referred to earlier was introduced in v2.8.4.
It's the ISAMLConfiguartionResolver under the ComponentSpace.SAML2.Configuration.Resolver namespace.
[/quote]

Is the configurationId something we would have to track internally with GET parameters (or setting it in the user's session so we don't have to update IDP's would be preferred)? Ideally, I was hoping we could rely on the entity ID since we only have one service provider configuration and ssl certificate.

public interface ISAMLConfigurationResolver
{
LocalIdentityProviderConfiguration GetLocalIdentityProviderConfiguration(string configurationID);
LocalServiceProviderConfiguration GetLocalServiceProviderConfiguration(string configurationID);
PartnerIdentityProviderConfiguration GetPartnerIdentityProviderConfiguration(string configurationID, string partnerName);
PartnerServiceProviderConfiguration GetPartnerServiceProviderConfiguration(string configurationID, string partnerName);
}

[quote]
ComponentSpace - 1/11/2018
We don't attempt to determine which SAML configuration should be used. This is the responsibility of the application.
In a multi-tenanted configuration there's one SAML configuration per tenant. Each configuration includes a unique configuration ID.
Prior to any SAML SSO API call, you need to specify the correct configuration ID so we know which configuration to use when processing the SAML SSO.
This is done by setting the SAMLController.ConfigurationID property.
For example:
SAMLController.ConfigurationID = "tenant1";
SAMLServiceProvider.ReceiveSSO(...);
This assumes that there's a SAML configuration whose ID is "tenant1".
Determining which configuration to use is the responsibility of the application.
If each tenant has a separate subdomain name, this can be used by the application as the tenant name (ie configuration ID).
[/quote]

Understood. Our use case is this, each of our clients has a URL such as

clientname.ourapplication.net

This has worked great until now, as the site URL allows us to inject the correct Entity Framework connection string for a specific site based on subdomain.

Now, we have a client who wants to have 10 or so contractors of theirs be allowed SSO.

I don't know that I trust our clients not to remove a GET parameter from our assertion consumer page, so I'd like to avoid that if possible. Would storing the database identifier of the SAML config in the session information be next the best solution in your opinion?

You could track the configuration ID using query string parameters, custom HTTP headers, cookies, ASP.NET session etc. It’s entirely up to you what makes the most sense for your application.
The subdomain name example I gave is quite common but certainly not the only way to do this.
If your URLs are of the form clientname.ourapplication.net, why can’t you use clientname as the configuration ID?
My apologies if I’m misunderstanding what you’re trying to do.

[quote]
ComponentSpace - 1/11/2018
You could track the configuration ID using query string parameters, custom HTTP headers, cookies, ASP.NET session etc. It's entirely up to you what makes the most sense for your application.
The subdomain name example I gave is quite common but certainly not the only way to do this.
If your URLs are of the form clientname.ourapplication.net, why can't you use clientname as the configuration ID?
My apologies if I'm misunderstanding what you're trying to do.
[/quote]

We currently use the client name as the connection ID basically. We distribute manuals, and until now, each client accessed their own manuals from our application. Now we have a client who has business partners that need to access manuals.

So let's say Walmart was a client, and wanted all of their vendors to login and access site evacuation plans for their stores.
- Walmart
- Trucking Company A
- Trucking Company B
- Security Company
- Etc

All of these companies will now need to authenticate against walmart.ourdomain.net. Until now it was simply walmart employees accessing walmart documents.

Would each of Walmart’s vendors have their own IdP?
Assuming so, your SAML configuration for Walmart would have multiple partner identity providers (eg one each of Walmart, trucking company A, trucking company B, security company).
You would still use the “walmart” subdomain name as the configuration ID when loading the SAML configuration.
SAMLController.ConfigurationID = “walmart”;
When you call SAMLServiceProvider.ReceiveSSO, our API determines which partner identity provider within the Walmart SAML configuration sent the SAML response and process it accordingly.

[quote]
ComponentSpace - 1/11/2018
Would each of Walmart's vendors have their own IdP?
Assuming so, your SAML configuration for Walmart would have multiple partner identity providers (eg one each of Walmart, trucking company A, trucking company B, security company).
You would still use the "walmart" subdomain name as the configuration ID when loading the SAML configuration.
SAMLController.ConfigurationID = "walmart";
When you call SAMLServiceProvider.ReceiveSSO, our API determines which partner identity provider within the Walmart SAML configuration sent the SAML response and process it accordingly.

[/quote]

Fantastic! This is exactly what I was hoping would work!

You’re welcome. :slight_smile: