Enabling SAML SP-Initiated web single sign-on (SSO)

By default, the WebSphere® Application Server SAML Trust Association Interceptor (TAI) supports IdP-initiated SSO. When custom code is in place, the SAML TAI can be configured to support SP-initiated SSO.

Before you begin

This task assumes that you have enabled your system to use the SAML web SSO feature. If you have not done this yet, see Enabling your system to use the SAML web single sign-on (SSO) feature.

About this task

This task provides an example class and the steps to configure SP-initiated SSO. The AuthnRequest must be Base64 encoded. This example uses the Base64 encoder that is provided in the com.ibm.websphere.wssecurity.wssapi.WSSUtilFactory API. You can use any Base64 encoder that you want. For more information, see javadoc for com.ibm.websphere.wssecurity.wssapi.WSSUtilFactory

Procedure

  1. 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 directory.
    • The com.ibm.ws.wssecurity.token.UTC class used in this sample can be found in the jar file, com.ibm.main.jar, located in (was_home)/plugins directory.
    The method getAuthnRequest(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 Single-Sign-On URL.
    AuthnRequestProvider.RELAY_STATE
    The relayState as defined by the SAML Web Browser single-sign-on profile.
    AuthnRequestProvider.REQUEST_ID
    The value for this key must match the ID attribute's value in the AuthnRequest message.
    AuthnRequestProvider.AUTHN_REQUEST
    A Base64 encoded AuthnRequest message as defined in the spec. Your code is responsible for generating the AuthnRequest message.
    
                    import java.util.ArrayList;
                    import java.util.HashMap;
                    import javax.servlet.http.HttpServletRequest;
                    import com.ibm.websphere.security.NotImplementedException;
                    import com.ibm.ws.wssecurity.token.UTC;
                    import com.ibm.wsspi.security.web.saml.AuthnRequestProvider;
                    import com.ibm.websphere.wssecurity.wssapi.WSSUtilFactory;
                    .........
    
                    public HashMap <String, String> getAuthnRequest(HttpServletRequest req, String errorMsg, 
    			   String acsUrl, ArrayList<String> ssoUrls)
    	        throws NotImplementedException {
    
                   
    	   
    	        //create map with following keys
    	        HashMap <String, String> map = new HashMap <String, String>();
    	       
    	        String ssoUrl = "https://example.com/saml20/Login";
    	        map.put(AuthnRequestProvider.SSO_URL, ssoUrl);
    
    	        String relayState = generateRandom();
    	        map.put(AuthnRequestProvider.RELAY_STATE, relayState);
    
    	        String requestId = generateRandom();
    	        map.put(AuthnRequestProvider.REQUEST_ID, requestId);
    	        
    	        //create AuthnRequest	        	        
    	        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=\"false\" IsPassive=\"false\""
    				   + "ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" "
    				   + "AssertionConsumerServiceURL=\"" +acs+"\" "
    				   + "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:transient\""
    				   +"SPNameQualifier=\"mysp\""
    				   +"AllowCreate=\"true\" /> <samlp:RequestedAuthnContext Comparison=\"exact\"> "
    				   +"<saml:AuthnContextClassRef xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">"
    			         +"urn:oasis:names:tc:SAML:2.0:ac:classes:</samlp:RequestedAuthnContext> </samlp:AuthnRequest>";
    
                   // get an instance of the WSSUtilFactory
                   WSSUtilFactory wssuf = WSSUtilFactory.getInstance();
    
                  // base64 encode the authn request string
                    String encAuthnMsg = wssuf.encode(authnMessage.getBytes());
    
                  //put the base64-encoded authn request in the map
                   map.put(AuthnRequestProvider.AUTHN_REQUEST, encAuthnMsg);
    
                   return map;
    	        }
                 private String generateRandom() {
                 //implement code that generates a random alpha numeric String that is unique
                 //each time it is invoked and cannot be easily predicted (like a counter)
                 }
  2. Put a jar file that contains your custom class in the (WAS_HOME)/lib/ext directory.
  3. Configure the SAML web SSO TAI to use your AuthnRequest message.
    1. Log on to the WebSphere Application Server administrative console.
    2. Click Security > Global security.
    3. Expand Web and SIP security and click Trust association.
    4. Click Interceptors.
    5. Click com.ibm.ws.security.web.saml.ACSTrustAssociationInterceptor
    6. For Custom properties, click new, then complete the following custom property information, where id is what you assigned to the SSO Service Provider (SP) for which you want this property to apply:
      • Name: sso_<id>.sp.login.error.page
      • Value: The class name of your custom AuthnRequestProvider implementation.