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:- Once you try to access the protected resource, the system redirects you to SSO entry point (samlsiglesignon extension, /saml/)
- SSO entry point generates a new authentication request using SAML 2.0 protocol, digitally sign it and send it to the OKTA.
- After authentication at OKTA with your account, you will be redirected back to hybris and automatically signed-in.
- 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.
- 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.

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

- 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.
- 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)
- Download the certificate here:
- Download metadata.xml (“Identity Provider metadata” link):
You will need to re-download it each time you change app settings. You need to restart hybris each time you replace metadata.xml
- Create a sample user
- 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)
keytool.exe -certreq -alias hybris -keystore samlKeystore.jks -file certificate.cer
sso.metadata.location = classpath:security/metadata.xml
sso.entity.id = urn:ssoextension:hybris:de
sso.keystore.location = security/samlKeystore.jks
sso.keystore.password = changeit
sso.keystore.privatekey.password = changeit
sso.keystore.default.certificate.alias = hybris
sso.keystore.privatekey.alias = hybris
sso.entity.id = urn:ssoextension:hybris:de
sso.keystore.location = security/samlKeystore.jks
sso.keystore.password = changeit
sso.keystore.privatekey.password = changeit
sso.keystore.default.certificate.alias = hybris
sso.keystore.privatekey.alias = hybris
Token processor
For ASM you need to do nothing: the token processing has already implemented in AssistedServiceFilter.java :protected void doFilterInternal(final HttpServletRequest httpservletrequest, final HttpServletResponse httpservletresponse,
final FilterChain filterchain) throws ServletException, IOException
{
if (AssistedServiceUtils.getSamlCookie(httpservletrequest) != null)
{
try
{
final LoginToken token = new CookieBasedLoginToken(AssistedServiceUtils.getSamlCookie(httpservletrequest));
// perform login only in case token doesn't belong to currently logged in agent
if (!getAssistedServiceFacade().isAssistedServiceAgentLoggedIn()
|| !getAssistedServiceFacade().getAsmSession().getAgent().getUid().equals(token.getUser().getUid()))
{
if (getAssistedServiceFacade().isAssistedServiceAgentLoggedIn())
{
getAssistedServiceFacade().logoutAssistedServiceAgent();
}
getAssistedServiceFacade().loginAssistedServiceAgent(httpservletrequest);
getAssistedServiceAgentLoginStrategy().login(token.getUser().getUid(), httpservletrequest, httpservletresponse);
getAssistedServiceFacade().emulateAfterLogin();
}
}
...
final FilterChain filterchain) throws ServletException, IOException
{
if (AssistedServiceUtils.getSamlCookie(httpservletrequest) != null)
{
try
{
final LoginToken token = new CookieBasedLoginToken(AssistedServiceUtils.getSamlCookie(httpservletrequest));
// perform login only in case token doesn't belong to currently logged in agent
if (!getAssistedServiceFacade().isAssistedServiceAgentLoggedIn()
|| !getAssistedServiceFacade().getAsmSession().getAgent().getUid().equals(token.getUser().getUid()))
{
if (getAssistedServiceFacade().isAssistedServiceAgentLoggedIn())
{
getAssistedServiceFacade().logoutAssistedServiceAgent();
}
getAssistedServiceFacade().loginAssistedServiceAgent(httpservletrequest);
getAssistedServiceAgentLoginStrategy().login(token.getUser().getUid(), httpservletrequest, httpservletresponse);
getAssistedServiceFacade().emulateAfterLogin();
}
}
...
....
final UserModel user = userService.getUserForUID(token.getUser().getUid());
....
final UserModel user = userService.getUserForUID(token.getUser().getUid());
....
Scott Maier
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());