Provide SSO with dynamic selection of IDPs in a multi-tenant Spring application
Today, most applications with private areas require users to provide credentials in order to log in and use the application. As the apps have grown in numbers, so have the users, which has caused the issue of keeping secure multiple credentials for different apps to surface.
Maintaining secure credentials can be scary and confusing at times. A popular solution to resolve this problem is Single-Sign-On (SSO), an authentication method that allows users to securely control multiple applications and websites using only one set of credentials.
Many customers now require development teams to make the use of SSO a basic prerequisite for their applications. Spring, following, as usual, the trends, has created extension modules to help developers achieve SSO log in on their apps.
In a multi-tenant Spring application, you can use Spring SAML to provide SSO. A separate identity provider is configured for each tenant. Also, every tenant uses different URLs to access the application. The question arising is how it dynamically identifies the correct identity provider given the URL used to access the application? Does Spring provide an easy and elegant way to achieve this or is it that a lot of development and configuration has to be done to achieve this?
The truth is, it depends on the version of Spring Security and Spring SAML you use and if it's worth upgrading Spring security to the newest version due to the dependency conflicts that may arise.
Spring Security 5.2, with Spring SAML at version 2
As of Spring Security 5.2, with Spring SAML at version 2, you can indicate the necessary asserting party metadata in the properties file.
You can set up multiple IDPs like:
- certificate-location: "classpath:tenant1-idp.crt"
This is all you need!
Spring Security minor to version 5.2, with Spring SAML at version 1
On minor Spring Security versions using Spring SAML version 1, multi-tenancy on IPDs isn't cleanly supported. Some configurations must be applied and code written to achieve the desired result.
In resources we populate all of our idps-metadata.xml files (each xml corresponds to the IDP metadata each tenant will use) under folder `saml`.
In a file that uses annotation `@Configuration` we create a `@Bean` of type `CachingMetadataManager` and from the above location we load all IDPs that the application will use.
The next step is to create a `CustomSAMLContextProviderImpl` class which extends `SAMLContextProviderImpl` and overrides its `populateGenericContext` method.
In this method we follow the steps:
- Create a custom `HttpServletRequestWrapper` class to get tenant's server name and scheme from the request URL.
- Calculate the tenant's entity id from tenant's server name and scheme
- Check if `MetadataManager#getSPEntityNames()` doesn't contain this tenant's entity id.
- Try to add a new `MetadataProvider`.
- Set `MetadataManager#setHostedSPName(String)` for the appropriate tenant.
In this short post, we've seen how we can configure and use separate Identity Providers for each tenant on a multi-tenant Spring application depending on the version of Spring Security and Spring SAML the application uses, to achieve SSO.
Take a look at our Web Design and Development services to see where we can help.