hybris/OKTA SSO integration


Situation

OKTA is a cloud-based SSO platform that allows users to enter one name and password to access multiple applications. Also it works as Identity Provider that is useful if you want to store credentials outside your service.

There are two user groups where SSO integration makes sense: business users and customers.

Complexity

Hybris OOTB doesn’t support any particular SSO providers. It has a module named samlsinglesignon which can be used for the integration with any SAML-compatible SSO services. However, this module is designed only for hybris assisted service module functionality (belongs to the call center features).

Solution

The video below shows the results of this experiment.

In the video OKTA is an external identity provider (hybrisLogin -> ”OKTA” button -> OKTA.com form -> OKTA credentials -> hybris e-shop, customer is authenticated)

Technical solution

Behind the scenes the interaction between OKTA and hybris look like this:

  1. Once you try to access the protected resource, the system redirects you to SSO entry point (samlsiglesignon extension, /saml/)
  2. SSO entry point generates a new authentication request using SAML 2.0 protocol, digitally sign it and send it to the OKTA.
  3. After authentication at OKTA  with your account, you will be redirected back to hybris and automatically signed-in.
  4. The samlsinglesignon extension listens to incoming requests (it is /samlsinglesignon/*). Once the extension receives a request from Okta, it checks if the request has a correct SAML assertion. If failed, the extension redirects the user back to the identity provider (Okta), and the user is asked to log in.
  5. Otherwise, it creates the secure cookie samlPassThroughToken and redirects the user to the URL of the protected resource. This cookie should be used for initiating the customer session by the website. the SSO functionality in hybris is preconfigured to work with ASM module, so there is a asmaddon that has a Filter that processes samlPassThroughToken, and set a session user if the token is found. We are not going to use ASM Addon in this solution, so we need to write our own processor that sets up the customer session based on the cookie from samlsinglesignon.

image2016-5-18 19-13-23.png

There are a number of edge cases that need to be supported in your code.  For example, the IDP session ends earlier or later than storefront’s session. In the “later” case the storefront should re-request the token and re-establish the authentication seamlessly without any data lost.You needn’t parse the token and authenticate the user if this user has already been authenticated. You need to support single sign out as well. User data provisioning is also needed if you use Okta as IDP. If you use the external IDP for more than one customer type, you need to support different types of sessions.

Configuring Okta

  • Request a developer account (trial) from okta.com
  • Create an app in the okta console. In my case it is “electronics.local”

image2016-5-18 19-13-41

Create new app -> SAML 2.0 -> Enter app name, then you need to specify two URLS:

  1. Single Sign on URL. Change your domain here, https://localhost:9002/samlsinglesignon/saml/SSO. It is very important to specify the correct domain and protocol here. 
  2. Audience URI (SP Entity ID) urn:ssoextension:hybris:de  (or you own SP Entity ID, you can change it in the configuration. I used the default value)
  3. Download the certificate here:
    image2016-5-18 19-14-7
  4. Download metadata.xml (“Identity Provider metadata” link):
    image2016-5-18 19-25-13.png
    You will need to re-download it each time you change app settings.  You need to restart hybris each time you replace metadata.xml
  5. Create a sample user
    image2016-5-18 19-15-44
  6. Assign the app to the user (this operation could be done automatically later using API, if needed)

Configuring samlsinglesignon extension

  • Create a jks file. The simplest way is to use the default keystore file that is included into the extension. The right way is to create it from scratch (see the documentation of keytool)

<br />
keytool.exe -certreq -alias hybris -keystore samlKeystore.jks -file certificate.cer<br />

Copy downloaded metadata.xml into the security folder or change sso.metadata.location to your own. Change the following parameters (I used the default values)

<br />
sso.metadata.location = classpath:security/metadata.xml<br />
sso.entity.id = urn:ssoextension:hybris:de<br />
sso.keystore.location = security/samlKeystore.jks<br />
sso.keystore.password = changeit<br />
sso.keystore.privatekey.password = changeit<br />
sso.keystore.default.certificate.alias = hybris<br />
sso.keystore.privatekey.alias = hybris<br />

Token processor

For ASM you need to do nothing: the token processing has already implemented in AssistedServiceFilter.java :

<br />
protected void doFilterInternal(final HttpServletRequest httpservletrequest, final HttpServletResponse httpservletresponse,<br />
final FilterChain filterchain) throws ServletException, IOException<br />
{<br />
if (AssistedServiceUtils.getSamlCookie(httpservletrequest) != null)<br />
{<br />
try<br />
{<br />
final LoginToken token = new CookieBasedLoginToken(AssistedServiceUtils.getSamlCookie(httpservletrequest));<br />
// perform login only in case token doesn't belong to currently logged in agent<br />
if (!getAssistedServiceFacade().isAssistedServiceAgentLoggedIn()<br />
|| !getAssistedServiceFacade().getAsmSession().getAgent().getUid().equals(token.getUser().getUid()))<br />
{<br />
if (getAssistedServiceFacade().isAssistedServiceAgentLoggedIn())<br />
{<br />
getAssistedServiceFacade().logoutAssistedServiceAgent();<br />
}<br />
getAssistedServiceFacade().loginAssistedServiceAgent(httpservletrequest);<br />
getAssistedServiceAgentLoginStrategy().login(token.getUser().getUid(), httpservletrequest, httpservletresponse);<br />
getAssistedServiceFacade().emulateAfterLogin();<br />
}<br />
}<br />
...<br />

For customers and cockpit administrators you need to set the session manually:

<br />
....<br />
final UserModel user = userService.getUserForUID(token.getUser().getUid());<br />
....<br />

Create a filter in the storefront extension which will check if the cookie is created and start the session by getting the user from the cookie

Note – This filter should be defined at the last in the filter chain of the storefront (or after springSecurityFilterChain) so that it gets called after the jsessionid is created.

© Rauf Aliev, June 2016

One Response

  1. Scott Maier

    Scott Maier

    Reply

    15 November 2017 at 08:19

    Do you have an example or can you explain this part with a little more detail?
    For customers and cockpit administrators you need to set the session manually:
    final UserModel user = userService.getUserForUID(token.getUser().getUid());

Leave a Reply