Enabling SAML Service Provider Initiated web SSO
To enable SAML Service Provider Initiated (SP-Initiated) web SSO (SSO), complete the following task.
- Develop a SAML authentication request provider that implements the
com.ibm.wsspi.security.web.saml.AuthnRequestProvider
interface.- The
com.ibm.wsspi.security.web.saml.AuthnRequestProvider
class is found in the was_public.jar file in the $WAS_HOME/dev folder. - The
com.ibm.ws.wssecurity.saml.common.util.UTC
class that is used in this sample can be found in the com.ibm.wsfp.main.jar file that is located in the $WAS_HOME/plugins folder. - The
HttpServletRequest
class that is used in this sample can be found in the $WAS_HOME/lib/j2ee.jar folder.
The methodgetAuthnRequest
(HttpServletRequest req
,String errorMsg
,String acsUrl
,ArrayList<String>
ssoUrls
) must return a map that includes four entries with the following keys.- AuthnRequestProvider.SSO_URL
- The SAML identity provider's SSO URL.
- AuthnRequestProvider.RELAY_STATE
- The relayState as defined by the SAML Web Browser SSO profile.
- AuthnRequestProvider.REQUEST_ID
- The value for this key must match the ID attribute's value in the AuthnRequest message.
- AuthnRequestProvider.AUTHN_REQUEST
- A Base 64 encoded AuthnRequest message as defined in the spec.
package com.ibm.saml; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Base64; import java.util.Date; import java.util.HashMap; import javax.servlet.http.HttpServletRequest; import com.ibm.websphere.security.NotImplementedException; import com.ibm.ws.wssecurity.saml.common.util.UTC; import com.ibm.wsspi.security.web.saml.AuthnRequestProvider; /** SAML authentication request provider implementation */ public class SAMLAuth implements AuthnRequestProvider { private static String printableChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; public HashMap<String, String> getAuthnRequest(HttpServletRequest req, String errorMsg, String acsUrl, ArrayList<String> ssoUrls) throws NotImplementedException { System.out.println("Create SAML AuthnRequest \n Date :: "+new Date()); HashMap<String, String> map = new HashMap<String, String>(); // ADFS server url String ssoUrl = "https://adfsserver.mdmce.local/adfs/ls"; map.put(AuthnRequestProvider.SSO_URL, ssoUrl); System.out.println("ssoUrl:: "+ssoUrl); String reqURI = req.getRequestURI(); System.out.println("RequestURI:: "+reqURI); System.out.println("ssoUrls:: "+ssoUrls); String scheme = req.getScheme(); // http String serverName = req.getServerName(); // hostname.com int serverPort = req.getServerPort(); // 80 String contextPath = req.getContextPath(); // /mywebapp String relayState = generateRandom(); // If the URL is ACS URL then do not set relayState parameter with constructed URL if(!reqURI.contains("samlsps") && !reqURI.contains("error")) { StringBuilder url = new StringBuilder(); url.append(scheme).append("://").append(serverName); if (serverPort != 80 && serverPort != 443) { url.append(":").append(serverPort); } url.append(contextPath); System.out.println("URL:: "+url.toString()); relayState=url.toString(); } map.put(AuthnRequestProvider.RELAY_STATE, relayState); System.out.println("RelayState:: "+relayState); String requestId = generateRandom(); map.put(AuthnRequestProvider.REQUEST_ID, requestId); System.out.println("RequestId:: "+requestId); String acsURL = "https://<ipaddress>:<portnumber>/samlsps/ipm"; String issuer = acsUrl; String destination = ssoUrl; System.out.println("acsURL:: "+acsURL); // create AuthnRequest- Authentications methods // urn:federation:authentication:windows // urn:oasis:names:tc:SAML:2.0:ac:classes:Password // urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos String authnMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" " + "ID=\"" + requestId + "\" Version=\"2.0\" " + "IssueInstant=\"" + UTC.format(new java.util.Date()) + "\" ForceAuthn=\"true\" IsPassive=\"false\"" + " ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" " + "AssertionConsumerServiceURL=\"" + acsURL + "\" " + "Destination=\"" + destination + "\"> " + "<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">" + issuer + "</saml:Issuer> <samlp:NameIDPolicy" + " Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress\"" + " SPNameQualifier=\"mysp\"" + " AllowCreate=\"true\" /> <samlp:RequestedAuthnContext Comparison=\"exact\"> " + "</samlp:RequestedAuthnContext> </samlp:AuthnRequest>"; System.out.println("Before encoding authnMessage :"+authnMessage); String encodedAuth = Base64.getEncoder().encodeToString(authnMessage.getBytes()); System.out.println("After encoding authnMessage :"+encodedAuth); map.put(AuthnRequestProvider.AUTHN_REQUEST, encodedAuth); return map; // Implement method to generate a random alpha numeric String that is unique // each time it is invoked and cannot be easily predicted (like a counter) private String generateRandom() { byte[] seed = SecureRandom.getSeed(16); SecureRandom random = new SecureRandom(seed); int modder = printableChars.length(); char[] result = new char[8]; for (int i = 0; i < 8; i++) { int j = random.nextInt(modder); result[i] = printableChars.charAt(j); } return new String(result); } @Override public String getIdentityProviderOrErrorURL(HttpServletRequest arg0, String arg1, String arg2, ArrayList<String> arg3) throws NotImplementedException { return null; } }
Note: If the SSO parameters section in the app_secrets.yaml file does not have SAML assertion, then the default value of the forceAuthn parameter isfalse
. - The
- In the custom class you can specify either urn:oasis:names:tc:SAML:2.0:ac:classes:Password as authentication context for password authentication OR urn:federation:authentication:windows for windows-based authentication.
- Put a JAR file that contains your custom class in the $WAS_HOME/lib/ext folder.
- Copy the com.ibm.wsfp.main.jar file in the
$WAS_HOME/lib/ext folder. The location of this JAR file is
$WAS_HOME/plugins.Note: For multiple node clusters, perform steps 3 and 4 above for all the nodes in the cluster.
- Configure the SAML web SSO TAI to use your AuthnRequest message.
- Log in to WebSphere® Application Server console.
- Click .
- Expand Web and SIP security and click Trust association.
- Click Interceptors.
- Click com.ibm.ws.security.web.saml.ACSTrustAssociationInterceptor.
- Under Custom properties, set the following property for the login error page.
ExampleName: sso_<id>.sp.login.error.page
Value: Fully qualified name of AuthnRequestProvider implementation class
com.ibm.saml.SAMLAuthRequest
-
Name: sso_1.sp.acsUrl Value: https://<hostname>:<sslport>/samlsps/<any URI pattern string>
Where hostname is the hostname of the system where the WebSphere Application Server is installed and sslport is the web server SSL port number (WC_defaulthost_secure).Examplehttps://host.ipm.in:9443/samlsps/ipm
- Set the following custom properties according to your configuration.
Name Value sso_1.sp.acsUrl https://ipmserver.ipm.in:9443/samlsps/ipm sso_1.sp.idMap idAssertion sso_1.idp_1.EntityID http://adfsserver.ipm.local/adfs/services/trust sso_1.sp.preventReplayAttackScope Server replayAttackTimeWindow 600 sso_1.sp.preventReplayAttack False sso_1.sp.useRealm http://adfsserver.ipm.local/adfs/services/trust sso_1.sp.preserveRequestState False sso_1.sp.filter request-url^=/|/mdm_ui sso_1.sp.login.error.page com.ibm.saml.SAMLAuthRequest sso_1.idp_1.SingleSignOnUrl https://adfsserver.ipm.local/adfs/ls/ Note:- The
SingleSignOnUrl
andEntityID
property are automatically added when you import federation metadata file. - In the case of multi-node cluster deployment with a load balancer, the ACS URL should point to the load balancer URL and port.
- The
- Restart the WebSphere Application Server.