SAP Hybris SAML Single Sign On

(I published this article on August 8, 2018 in Linkedin)

For our latest finished project we had to implement SSO from SharePoint to SAP Hybris for store customers. We had difficulties with that because of lack of information about Hybris SSO. Here, in this article i will share how we configured it(customizing the SAML extension part), i think it will be helpful for others who face the same difficulties.

First of all i recommend you to read this article to understand what SAML is and how it works: https://www.gigya.com/blog/the-basics-of-saml/

So, in our situation we have SSO from SharePoint to SAP Hybris. Our ID(Identity Provider) is AD(Active Directory) and SPs(Service providers) are SharePoint and SAP Hybris.

SP: SAP Hybris, Sharepoint

ID: AD

Customizing the SAML Extension Behavior

Hybris has a module samlsinglesignon which can be used for integration with any SAML SSO services. However, this module is designed only for assisted service module. We used the module samlsinglesignon but customized it for store customers.

1. Create a new extension with name <CUSTOM>singlesignon using yempty(for extension template)

  • Append the new extension to config/localextensions.xml and run ant all
<extensions>
...
<extension name="<CUSTOM>singlesignon" />
</extensions>

2. Set the dependency to samlsinglesignon

  • Tell the build framework that the <CUSTOM>singlesignon extension depends on the samlsinglesignonextension. To do so, open <CUSTOM>singlesignon/extensioninfo.xml and add the following line as the first element within the extension element.
<requires-extension name="samlsinglesignon"/>

Run ant all

3. Add following properties in project.properties of your <CUSTOM>singlesignon extension

sso.mapping.customergroup.usertype=Customer   sso.mapping.customergroup.groups=customergroup   sso.cookie.name=samlPassThroughToken   sso.usergroup.attribute.key=usergroup   sso.firstname.attribute.key=FirstName   sso.lastname.attribute.key=LastName   sso.userid.attribute.key=mail

4. Add following properties in local.properties(in our project we have three servers — development, quality and production. So we have three local.properties files for the servers and as you can guess we added the properties for each server with different values).

sso.redirect.url=https://${<url of your env>}:9002/    sso.entity.id=ssoextension:<custom>:com

5.Create <CUSTOM>SsoFilter

6.Create bean <CUSTOM>SsoFilter and add ref in your spring-filter-config.xml(where you create your beans for filters)

<bean id="<CUSTOM>SsoFilter" class="com.<CUSTOM>.storefront.filters.<CUSTOM>SsoFilter">
<property> -- Here you can add your properties
</bean>
<ref bean="<CUSTOM>SsoFilter"/>

5. Create <CUSTOM>SsoServiceUtils.java to read and erase cookie(you can take a look at how it is done in AssistedServiceUtils — getSamlCookie and eraseSamlCookie methods).

6. Create <CUSTOM>AuthenticationToken(take a look at AssistedSericeAuthenticationToken).

7. In your earlier created <CUSTOM>SsoFilter make authentication object, set in security context for proper access and then call success handler to set details for the user and eraseSamlCookie to avoid infinite redirects.

if (<CUSTOM>SsoServiceUtils.getSamlCookie(httpservletrequest) != null){
final LoginToken token = new CookieBasedLoginToken(<CUSTOM>SsoUtilsUtils.getSamlCookie(httpservletrequest));
String userId = token.getUser().getUid();
UserModel userModel = userService.getUserForUID(userId);
userService.setCurrentUser(userModel); <CUSTOM>AuthenticationToken <CUSTOM>AuthenticationToken = <CUSTOM>SsoLoginStrategy.login(userId, httpservletrequest, httpservletresponse); authenticationSuccessHandler.onAuthenticationSuccess(httpservletrequest, httpservletresponse, <CUSTOM>AuthenticationToken);
}
filterchain.doFilter(httpservletrequest, httpservletresponse);

In login method we have:

..
final UserDetails userDetails = getUserDetailsService().loadUserByUsername(username);
final <CUSTOM>AuthenticationToken <CUSTOM>AuthenticationToken = new <CUSTOM>AuthenticationToken(username, userDetails.getAuthorities());
<CUSTOM>AuthenticationToken.setDetails(new WebAuthenticationDetails(request)); getGuidCookieStrategy().setCookie(request, response);
SecurityContextHolder.getContext().setAuthentication(<CUSTOM>AuthenticationToken);
<CUSTOM>ServiceUtils.eraseSamlCookie(response);
..

8. If you receive time difference error between SP and IDP just add the property responseSkew to the WebSSOProfileConsumerImpl and SingleLogoutProfileImpl beans:

bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl">
<property name="responseSkew" value="600"/> <!-- 10 minutes -->
</bean>
<bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl">
<property name="responseSkew" value="600"/> <!-- 10 minutes -->
</bean>

9. For SAML requests forwarded by a reverse-proxy or a load balance follow these steps:

  • Provide information about front-end URL to the back-end servers by changing the contextProvider bean implementation in your customize\….\spring-security-config.xml to class org.springframework.security.saml.context.SAMLContextProviderLB:
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB">
<property name="scheme" value="https"/>
<property name="serverName" value="${saml.server.name}"/>
<property name="serverPort" value="${saml.server.port}"/>
<property name="includeServerPortInRequestURL" value="${saml.include.server.port.inrequest.url}"/>
<property name="contextPath" value="/samlsinglesignon"/>
</bean>

This setting enables the extension to correctly form all generated URLs and verify endpoints of the incoming SAML messages.

  • In case you use automatically generated metadata(as in our case) make sure to configure entityBaseURL matching the front-end URL in your metadataGeneratorFilter bean in your customize\….\spring-security-config.xml:
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter"><constructor-arg><bean class="org.springframework.security.saml.metadata.MetadataGenerator"><property name="entityId" value="${sso.entity.id:urn:ssoextension:hybris:de}"/><property name="entityBaseURL" value="${saml.sso.metadata-generator.entity-base-url}"/></bean></constructor-arg>
</bean>
  • In your local.properties files add following properties(here we have the same situation with servers and local.properties — development, quality and production):
saml.sso.metadata-generator.entity-base-url=https://<url of your env>:9002/samlsinglesignon
saml.server.name=<url of your env>
saml.server.port=9002 // port
saml.include.server.port.inrequest.url=true/false

For more info about reverse-proxy or a load balance you can read: https://docs.spring.io/autorepo/docs/spring-security-saml/1.0.x/reference/html/configuration-advanced.html

Make sure you give this post a clap and follow my blog if you find it helpful.

Senior Software Developer / Hybris Subject Matter Expert / Founder