ADFS + DXP

Today's "I wonder if I can do this" experiment ended up being SAML integration with ADFS using Liferay as a service provider. I was surprised how quickly we were able to set it up (if you don't count the day spent trying to make it work on WeDeploy). For anyone looking to do similar, here's what we did...

I should note at this point that the DXP installation I used was provided by Firelay and so the configuration present which made this straightforward was already in place before I got there.

First off, install the SAML plugin (https://web.liferay.com/marketplace/-/mp/application/15188711), and second, read the tutorial (https://dev.liferay.com/discover/portal/-/knowledge_base/6-2/integrating-existing-users-into-liferay#saml). I've never actually SAML integrated anything before, so the docs were essential for me. Filling in the details and adding the metadata + details from the IdP is really straight forward, we just left the defaults. For simplicity we left the Entity ID as the domain LifeRay was running on (www.my-site.com, or whatever).

Before enabling the SAML plugin it's wise to add a "sign in" page (with the standard sign-in portlet) to the main site, so that if things go wrong and the login session expires before they're fixed you can still log-in and fix it again using Liferay's local authentication.

At the ADFS end there are a couple of bits that need setting to make Liferay happy, at this stage I deferred to a colleague who administers ADFS who reliably told me that those things are...

1. Set up the claim rules

If using email address rather than screenName as the user identifier, set up the following two claim rules to map the AD Email address to the SAML NameID:

1a. Map the email address:

 

or in CRL:

c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]

 => issue(store = "Active Directory", types = ("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"), query = ";mail;{0}", param = c.Value);

1b. Convert the email address to NameID: 

or in CRL:

c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]

 => issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");

2. Make ADFS sign the message

Run Powershell command to make AD FS sign the whole message:

Set-AdfsRelyingPartyTrust -TargetName "www.my-site.com" -SamlResponseSignature MessageAndAssertion

After that, you should be good to go.

We saw a couple of errors along the way, so in case it's useful to anyone...

On WeDeploy we encountered a mismatch between the endpoints:

09:47:39,611 ERROR [http-nio-8080-exec-25][BaseSAMLMessageDecoder:215] SAML message intended destination endpoint 'https://my-site.wedeploy.io/c/portal/saml/acs' did not match the recipient endpoint 'http://my-site.wedeploy.io/c/portal/saml/acs'

09:47:39,611 ERROR [http-nio-8080-exec-25][BaseSamlStrutsAction:46] com.liferay.saml.SamlException: org.opensaml.xml.security.SecurityException: SAML message intended destination endpoint did not match recipient endpoint

com.liferay.saml.SamlException: org.opensaml.xml.security.SecurityException: SAML message intended destination endpoint did not match recipient endpoint

This is a result of how WeDeploy is configured, specifically the communication between the front end web server and Tomcat. To my non-expert understanding the config effectively makes Liferay think that traffic which reaches your domain on HTTPS ictually coming in on HTTP, hence the mismatch message.

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" URIEncoding="UTF-8" />

It's worth noting that we were never able to complete the WeDeploy configuration on account of this error, but is was very useful as a learning exercise.

Also, if you forget to sign the message from ADFS (step 2) you'll see the following:

11:25:01,397 ERROR [http-nio-8443-exec-9][MandatoryAuthenticatedMessageRule:37] Inbound message issuer was not authenticated.

11:25:01,402 ERROR [http-nio-8443-exec-9][BaseSamlStrutsAction:46] com.liferay.saml.SamlException: org.opensaml.ws.security.SecurityPolicyException: Inbound message issuer was not authenticated.

com.liferay.saml.SamlException: org.opensaml.ws.security.SecurityPolicyException: Inbound message issuer was not authenticated.

Hope this is useful for anyone doing similar things.