There are requirements where ELM has to be deployed in an environment for users from different companies to collaborate. And in this scenario the end users would need to authenticate against different SAML IDPs which could be their respective company owned IDPs. Can we configure multiple Identity Providers with IBM Engineering Lifecycle Management Solution?
You can setup ELM to authenticate via Jazz Authorization Server (JAS) which is based on WebSphere Liberty. And using the SamlWeb 2.0 feature Liberty can be configured to further delegate the user authentication to a Third Party SAML IdP.
When JAS is configured with multiple OIDC Providers, Liberty presents a Social Media Selection Form to pick from a list of configured Providers. Although when configured with Multiple SAML IdPs a similar list is not available, we can depend on AuthFilters or we can modify the deployment to include another Liberty server and create unique filters to configure Authentication against Multiple SAML IdPs, and this is the focus of the article.
There are two Use Cases for configuring JAS with multiple SAML IdPs
Use Case 1
: Utilize Liberty SAML config AuthFilters to redirect to multiple SAML IdPs
Use Case 2
: When none of the available AuthFilters works for your deployment, install an additional Liberty server Use Case 2
The high level instructions to configure ELM with SAML IdPs:
The instructions for configuring JAS with a SAML IdP and a Third Party OIDC Provider are documented in the following articles
Sample Deployment
First step is to configure ELM and JAS. You would need to either configure JAS with multiple LDAPs, one each for a SAML IDP, or a consolidated LDAP server which has a copy of all the Users from multiple SAML IDPs.
User Groups to Jazz Roles mappings (JazzAdmins, JazzUsers etc) are picked from JTS configuration when configured with JAS. When Users accesses an ELM application URL, they are redirected to JAS for Authentication. Post successful authentication JTS performs one of the following:
com.ibm.team.repository.service.jts.internal.userregistry.scim.SCIMUserRegistryProvider
for User group to Jazz role mappings
com.ibm.team.repository.service.jts.internal.userregistry.ldap.LDAPUserRegistryProvider
for User group to Jazz role mappings.
Note: Special Subjects like ALL_AUTHENTICATED_USERS or NESTED_GROUPS would not work with JAS based deployments
Create a new Liberty Server using the JAS installation on port 9644
for Multiple OpenId Connect Providers and SAML Configurations.
The following examples uses JAS to create another server and copy over LDAP and SSL keystore files
cd [JAS_HOME]/wlp/bin
./server create samlop
cd [JAS_HOME]/wlp/usr/servers/samlop
mkdir defaults
cp ../jazzop/defaults/ldapUserRegistry.xml defaults/
cd [JAS_HOME]/wlp/usr/servers/samlop
cp ../jazzop/ibm-team.keystore .
jvm.options
and set heap to 4GB cd [JAS_HOME]/wlp/usr/servers/samlop
vi jvm.options
-Xmx4G -Xms4G -Xmn1G
Change directory to [JAS_HOME]/wlp/usr/servers/samlop
and edit server.xml
(delete old content). Enable features, LDAP, SSL configurations and set port to 9644
<?xml version="1.0" encoding="UTF-8"?> <server description="SAML_OP"> <featureManager> <feature>openidConnectServer-1.0</feature> <feature>appSecurity-2.0</feature> <feature>ldapRegistry-3.0</feature> <feature>ssl-1.0</feature> <feature>samlWeb-2.0</feature> </featureManager> <httpEndpoint host="*" httpPort="9281" httpsPort="9644" id="defaultHttpEndpoint"/> <keyStore id="defaultKeyStore" location="ibm-team.keystore" type="JCEKS" password="{xor}Nj0ycis6PjI="/> <ssl id="defaultSSLConfig" keyStoreRef="defaultKeyStore" trustStoreRef="defaultKeyStore" serverKeyAlias="sslkey" clientAuthenticationSupported="false"/> <include location="ldapUserRegistry.xml" optional="true"/> <!-- To grant all authenticated users access to the OIDC protected resource, grant them the oauth-role authenticated --> <oauth-roles> <authenticated> <special-subject type="ALL_AUTHENTICATED_USERS"/> </authenticated> <clientManager> <group name="JazzAdmins" /> </clientManager> </oauth-roles> </server>
Append server.xml
file, create a openidConnectProvider
config for each SAML IDP. Following is an example for 2 SAML IdPs
<openidConnectProvider id="op1" oauthProviderRef="oauth1" sessionManaged="true" signatureAlgorithm="RS256" /> <oauthProvider id="oauth1" > <localStore> <client name="SamlClientID_1" secret="SamlPassword_1" displayname="OpenID Provider SAML1" scope="openid profile email general" preAuthorizedScope="openid profile email general" redirect="https://<Jazz_Auth_server>/ibm/api/social-login/redirect/samlop1" /> </localStore> </oauthProvider> <openidConnectProvider id="op2" oauthProviderRef="oauth2" sessionManaged="true" signatureAlgorithm="RS256" /> <oauthProvider id="oauth2" > <localStore> <client name="SamlClientID_2" secret="SamlPassword_2" displayname="OpenID Provider SAML2" scope="openid profile email general" preAuthorizedScope="openid profile email general" redirect="https://<Jazz_Auth_server>/ibm/api/social-login/redirect/samlop2" /> </localStore> </oauthProvider>
id
and oauthProviderRef
are unique per OpenId Provider configuration
name
, secret
and redirect
URL ID will be used in the Social Login config on JAS
Import each SAML IDP metadata by copying it to [JAS_HOME]/wlp/usr/servers/samlop/resources/security
Append server.xml
file, configure multiple SAML IdP redirect configurations and set AuthFilters to incoming JAS URLs based on the id
created in the previous step. Following is an example for 2 SAML configurations.
<samlWebSso20 id="defaultSP" enabled="false" > </samlWebSso20> <samlWebSso20 id="samlSP1" spCookieName="jazzop_sso_cookie_idp_1" forceAuthn="true" idpMetadata="[JAS_HOME]/wlp/usr/servers/samlop/resources/security/samlidp1.xml" keyStoreRef="defaultKeyStore" keyAlias="sslkey" enabled="true" authFilterRef="samlAuthFilter1" > </samlWebSso20> <authFilter id="samlAuthFilter1"> <requestUrl id="samlRequestUrl" urlPattern="/op1/authorize" matchType="contains" /> </authFilter> <samlWebSso20 id="samlSP2" spCookieName="jazzop_sso_cookie_idp_2" forceAuthn="true" idpMetadata="[JAS_HOME]/wlp/usr/servers/samlop/resources/security/samlidp2.xml" keyStoreRef="defaultKeyStore" keyAlias="sslkey" enabled="true" authFilterRef="samlAuthFilter2" > </samlWebSso20> <authFilter id="samlAuthFilter2"> <requestUrl id="samlRequestUrl" urlPattern="/op2/authorize" matchType="contains" /> </authFilter>
op1
and op2
used in urlPattern
are the =id='s created in the openidConnectProvider configuration
Start the Liberty Server using the command [JAS_HOME}/start-jazz
Export SP metadata to be shared with each SAML IDP. samlSP1
and samlSP2
are IDs created in the samlWebSso20 config.
https://[Jazz_Auth_Server]:9644/ibm/saml20/samlSP1/samlmetadata
https://[Jazz_Auth_Server]:9644/ibm/saml20/samlSP2/samlmetadata
These files need to imported as Relying Party on the respective SAML IdPs
Note: If the server is to be configured behind Reverse Proxy, access the above URLs with the Reverse Proxy URLs.
In the Endpoint URLs below op1
and op2
are ids
created in the openidConnectProvider configuration. Test by accessing the following URLs, if they are redirected to a different SAML Idp
https://[Jazz_Auth_server]:9644/oidc/endpoint/op1/authorize
https://[Jazz_Auth_server]:9644/oidc/endpoint/op2/authorize
Note: If the Liberty server is to be configured behind Reverse Proxy, access the above URLs with the Reverse Proxy URLs.
Now that a Liberty Server is configured with multiple SAML configurations and also multiple OpenId Connect Providers (OP), we will redirect JAS to each OP by configure multiple Social Login configurations, creating an oidcLogin
config for each OP configured in the new liberty server
server.xml
file and add the following features within featureManager
<feature>socialLogin-1.0</feature> <feature>appSecurity-2.0</feature>
samlWeb-2.0
feature enabled on JAS, it can be disabled
<!-- Social Login Configuration to redirect to Liberty server configured with Multiple IDPs --> <oidcLogin id="samlop1" displayName="SAML IDP A" clientId="SamlClientID_1" clientSecret="SamlPassword_1" discoveryEndpoint="https://<Jazz_Auth_server>:9644/oidc/endpoint/op1/.well-known/openid-configuration" scope="openid profile email general" userNameAttribute="sub" trustStoreRef="defaultKeyStore" authFilterRef="OPAuthFilter1" mapToUserRegistry="true" > </oidcLogin> <authFilter id="OPAuthFilter1"> <requestUrl id="OPRequestUrl1" urlPattern="/authorize" matchType="contains" /> <userAgent id="OPUserAgent1" agent="Mozilla|Opera" matchType="contains"/> </authFilter> <oidcLogin id="samlop2" displayName="SAML IDP B" clientId="SamlClientID_2" clientSecret="SamlPassword_2" discoveryEndpoint="https://<Jazz_Auth_server>:9644/oidc/endpoint/op2/.well-known/openid-configuration" scope="openid profile email general" userNameAttribute="sub" trustStoreRef="defaultKeyStore" authFilterRef="OPAuthFilter2" mapToUserRegistry="true" > </oidcLogin> <authFilter id="OPAuthFilter2"> <requestUrl id="OPRequestUrl2" urlPattern="/authorize" matchType="contains" /> <userAgent id="OPUserAgent2" agent="Mozilla|Opera" matchType="contains"/> </authFilter>
id
is picked from the OP redirect
URL configured in Liberty server, example /samlop1
clientId
and clientSecret
is picked from name
and secret parameters from =oauthProvider
on the Liberty Server
displayName
is the name shown on the Liberty Social Media Selection form
Note: If the Liberty server is to be configured behind Reverse Proxy, update the discoveryEndpoint
URLs with the Reverse Proxy URLs.
The Liberty server start and stop would be in parallel with JAS (jazzop
starts first followed by samlop
).
[JAS_HOME}/start-jazz
[JAS_HOME}/stop-jazz
On accessing the ELM Application URI you woould be presented with the following Selection Form
Here is an example of a merged plugin config for IBM HTTP Server
<!-- Jazz Authorization Server - jazzop --> <VirtualHostGroup Name="default_host_jazzop"> <VirtualHost Name="*:80"/> <VirtualHost Name="*:443"/> </VirtualHostGroup> <ServerCluster CloneSeparatorChange="false" GetDWLMTable="false" IgnoreAffinityRequests="true" LoadBalance="Round Robin" Name="jazzop_default_node_Cluster" PostBufferSize="0" PostSizeLimit="-1" RemoveSpecialHeaders="true" RetryInterval="60" ServerIOTimeoutRetry="-1"> <Server CloneID="609d6ec4-46b5-4652-a377-76e8a1d0743c" ConnectTimeout="5" ExtendedHandshake="false" LoadBalanceWeight="20" MaxConnections="-1" Name="default_node_jazzop" ServerIOTimeout="900" WaitForContinue="false"> <Transport Hostname="[JAS_IP]" Port="9280" Protocol="http"/> <Transport Hostname="[JAS_IP]" Port="9643" Protocol="https"> <Property Name="keyring" Value="/opt/IBM/WebSphere/Plugins/config/webserver1/plugin-key.kdb"/> <Property Name="stashfile" Value="/opt/IBM/WebSphere/Plugins/config/webserver1/plugin-key.sth"/> </Transport> </Server> <PrimaryServers> <Server Name="default_node_jazzop"/> </PrimaryServers> </ServerCluster> <UriGroup Name="default_host_jazzop_default_node_Cluster_URIs"> <Uri AffinityCookie="JSESSIONID" AffinityURLIdentifier="jsessionid" Name="/jazzop/*"/> <Uri AffinityCookie="JSESSIONID" AffinityURLIdentifier="jsessionid" Name="/oidc/endpoint/jazzop/*"/> <Uri AffinityCookie="JSESSIONID" AffinityURLIdentifier="jsessionid" Name="/ibm/api/social-login/*"/> </UriGroup> <Route ServerCluster="jazzop_default_node_Cluster" UriGroup="default_host_jazzop_default_node_Cluster_URIs" VirtualHostGroup="default_host_jazzop"/> <!-- Jazz Authorization Server - New Liberty Server /samlop --> <VirtualHostGroup Name="default_host_samlop"> <VirtualHost Name="*:80"/> <VirtualHost Name="*:443"/> </VirtualHostGroup> <ServerCluster CloneSeparatorChange="false" GetDWLMTable="false" IgnoreAffinityRequests="true" LoadBalance="Round Robin" Name="samlop_default_node_Cluster" PostBufferSize="0" PostSizeLimit="-1" RemoveSpecialHeaders="true" RetryInterval="60" ServerIOTimeoutRetry="-1"> <Server CloneID="26d51d70-2025-4aed-8822-cdff0f1d93bd" ConnectTimeout="5" ExtendedHandshake="false" LoadBalanceWeight="20" MaxConnections="-1" Name="default_node_samlop" ServerIOTimeout="900" WaitForContinue="false"> <Transport Hostname="[JAS_IP]" Port="9281" Protocol="http"/> <Transport Hostname="[JAS_IP]" Port="9644" Protocol="https"> <Property Name="keyring" Value="/opt/IBM/WebSphere/Plugins/config/webserver1/plugin-key.kdb"/> <Property Name="stashfile" Value="/opt/IBM/WebSphere/Plugins/config/webserver1/plugin-key.sth"/> </Transport> </Server> <PrimaryServers> <Server Name="default_node_samlop"/> </PrimaryServers> </ServerCluster> <UriGroup Name="default_host_samlop_default_node_Cluster_URIs"> <Uri AffinityCookie="JSESSIONID" AffinityURLIdentifier="jsessionid" Name="/samlop/*"/> <Uri AffinityCookie="JSESSIONID" AffinityURLIdentifier="jsessionid" Name="/oidc/endpoint/samlop/*"/> <Uri AffinityCookie="JSESSIONID" AffinityURLIdentifier="jsessionid" Name="/oidc/endpoint/op1/*"/> <Uri AffinityCookie="JSESSIONID" AffinityURLIdentifier="jsessionid" Name="/oidc/endpoint/op2/*"/> <Uri AffinityCookie="JSESSIONID" AffinityURLIdentifier="jsessionid" Name="/ibm/saml20/*"/> </UriGroup> <Route ServerCluster="samlop_default_node_Cluster" UriGroup="default_host_samlop_default_node_Cluster_URIs" VirtualHostGroup="default_host_samlop"/>
You can configure Socal Login Web Application to include Local Authentication in the Selection form.
Add the following configuration along with all the oidcLogin
configurations:
<socialLoginWebapp enableLocalAuthentication="true" />
Here is the updated selection form when Local Authentication is included
Status icon key: