4

We have a product that has one customer and we implemented SAML flow for this customer using Spring Security SAML when we act as the service provider and the idp is in the customer side.

Now we have another customer that also want the authentication to be with SAML and we want the same SP to implement the SAML flow for this customer, also the second customer will have 2 flows for SAML one for mobile device and one for other devices using the same IDP. The IDPs of the two customer are different.

The problem

There are some differences between the two customers for example the assertion attributes are different and the action on success authentication is different, currently we provide our own implementation.

Also there might be more changes like different bindings etc...

My question is what is the best option/best practice to support such scenario and to be able to extend my SP to support more SAML flows with differences in the Assertion attributes and more configurations?

When we use Spring SAML should we use different Spring Security context files for each of the SAML flavors?

Are there issues with thread safety when using multiple contexts in parallel?

ilay zeidman
  • 2,654
  • 5
  • 23
  • 45

1 Answers1

2

My question is what is the best option/best practice to support such scenario and to be able to extend my SP to support more SAML flows with differences in the Assertion attributes and more configurations?

To branch certain configurations such as the Assertion attributes you will need to create separate Service Providers. Other configurations and services can be shared. Other configurations should be shared. For instance, I use single custom SAMLUserDetailsService implementation that pulls the unique EntityID out of the credential and uses that to map the SAML attributes differently for each IDP.

When we use Spring SAML should we use different Spring Security context files for each of the SAML flavors? Are there issues with thread safety when using multiple contexts in parallel?

I do not recommend running multiple security contexts separately. In my experience, there is a lot of configuration involved in Spring SAML and chances are, you will have to duplicate a ton of code needlessly by doing it this way.

In Spring SAML, there is the concept of using different aliases for different Service Providers. I have setup a many Service Providers to many IDPs and been able to use one Spring Security context and implement custom services where I need to to handle the differences. I do not have a full list of your requirements and there may be some that simply cannot be done within a single spring security context, but I'd wait to make sure that's the case before taking that route.

What specifically are will need to be different between each IDP?

I am limited in what code I am allowed to post, but I've included what I can.

  • Entry Point URL - If you have multiple IDPs with an alias set in your configuration, the entry point url by default will be

    "/saml/login/alias/" +productAlias+ "?idp=" + entityId;

    If you are behind a load balancer, you can configure it to rewrite any URL you want to the URL for the customer.

  • Bindings and Assertions - These are configured in each of the Service Providers metadata.xml file and can be different for each customer. The real challenge is how to pull the attributes out of the authenticated SAML request and get it in a usable form.

    I don't know if there is a better way to do this, but my requirement was to have any bindings mappable and configurable for any IDP I have configured. To accomplish this, I implemented a custom SAMLUserDetailsService. From the SAMLCredential passed in to the service, you can get use credential.getRemoteEntityID() to pull up the mapping for the customer. From there you'll need to parse out the attributes from the credential.

Example of parsing the SAML attributes for Microsoft and other IDPs

 public class AttributeMapperImpl implements AttributeMapper {

    @Override
    public Map<String, List<String>> parseSamlStatements(List<AttributeStatement> attributeList) {
        Map<String, List<String>> map = new HashMap<>();
        attributeList.stream().map((statement) -> parseSamlAttributes(statement.getAttributes())).forEach((list) -> {
            map.putAll(list);
        });
        return map;
    }

    @Override
    public Map<String, List<String>> parseSamlAttributes(List<Attribute> attributes) {
        Map<String, List<String>> map = new HashMap<>();
        attributes.stream().forEach((attribute) -> {
            List<String> sList = parseXMLObject(attribute.getAttributeValues());
            map.put(attribute.getName(), sList);
        });
        return map;
    }

    @Override
    public List<String> parseXMLObject(List<XMLObject> objs) {
        List<String> list = new ArrayList<>();

        objs.stream().forEach((obj) -> {
            if(obj instanceof org.opensaml.xml.schema.impl.XSStringImpl){
                XSStringImpl xs = (XSStringImpl) obj;
                list.add(xs.getValue());
            }else if(obj instanceof org.opensaml.xml.schema.impl.XSAnyImpl){
                XSAnyImpl xs = (XSAnyImpl) obj;
                list.add(xs.getTextContent());
            }
        });  

        return list;
    }

    @Override
    public String parseSamlStatementsToString(Map<String, List<String>> map) {
        String values = "";
        Iterator it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry) it.next();
            values += pair.getKey() + "=" + pair.getValue() + " ";
            it.remove(); // avoids a ConcurrentModificationException
        }
        return values;
    }

}
  • Action on Success/Failure - There are many possible ways of doing this. I chose to use a single endpoint in a controller that has access to the session that all requests go to on success. After successful authentication, I can pull out of the session what IDP the user is from and redirect them accordingly. Failure is a bit more difficult because it's entirely possible and likely that some failures will be so severe that you won't know which IDP the request came from (i.e. if the saml message is signed with the wrong cert).
blur0224
  • 972
  • 8
  • 26
  • The differences between the idps are the entry point url, the bindings, the action on success and failure and the assertion... can you share a samle code that demonstrate it? – ilay zeidman Jul 14 '16 at 01:04