Learn how to set up and authenticate end-users (not AEM authors) to a SAML 2.0 compatible IDP of your choosing.
SAML 2.0 integration with AEM Publish (or Preview), allows end users of an AEM-based web experience to authenticate to a non-Adobe IDP (Identity Provider), and access AEM as a named, authorized user.
AEM Author | AEM Publish | |
---|---|---|
SAML 2.0 support | ✘ | ✔ |
The typical flow of an AEM Publish SAML integration is as follows:
/system/sling/login
) that explicitly requests the login action.login-token
cookie on the HTTP response, which is used to authenticate subsequent requests to AEM Publish.saml_request_path
cookie.This video walks through of setting up SAML 2.0 integration with AEM as a Cloud Service Publish service, and using Okta as the IDP.
The following are required when setting up SAML 2.0 authentication:
SAML 2.0 is only supported to authenticate uses to AEM Publish or Preview. To manage the authentication of AEM Author using and IDP, integrate the IDP with Adobe IMS.
The IDP’s public certificate is added to AEM’s Global Trust Store, and used to validate the SAML assertion sent by the IDP is valid.
.../saml_login
) that includes the signed SAML assertion.Obtain the public certificate file from the IDP. This certificate allows AEM to validate the SAML assertion provided to AEM by the IDP.
The certificate is in PEM format, and should resemble:
-----BEGIN CERTIFICATE-----
MIIC4jCBAcoCCQC33wnybT5QZDANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJV
...
m0eo2USlSRTVl7QHRTuiuSThHpLKQQ==
-----END CERTIFICATE-----
Log in to AEM Author as an AEM Administrator.
Navigate to Tools > Security > Trust Store.
Create or open the Global Trust Store. If creating a Global Trust Store, store the password some place safe.
Expand Add certificate from CER file.
Select Select Certificate File, and upload the certificate file provided by the IDP.
Leave Map Certificate to User blank.
Select Submit.
The newly added certificate appears above the Add certificate from CRT file section.
Make note of the alias, as this value is used in the SAML 2.0 Authentication Handler OSGi configuration.
Select Save & Close.
The Global Trust Store is configured with the IDP’s public certificate on AEM Author, but since SAML is only used on AEM Publish, the Global Trust Store must be replicated to AEM Publish for the IDP public certificate to be accessible there.
Global Trust Store
1.0.0
com.your.company
/etc/truststore
./etc/truststore
) to AEM Publish.Creating a keystore for authentication-service is required when the SAML 2.0 authentication handler OSGi configuration property handleLogout
is set to true
or when AuthnRequest signing/SAML assertion ecryption is required
Installing the AEM public/private key pair is optional
AEM Publish can be configured to sign AuthnRequests (to IDP), and encrypt SAML assertions (to AEM). This is achieved by providing a private key to AEM Publish, and it’s matching public key to the IDP.
The AuthnRequest (the request to the IDP from AEM Publish that initiates the login process) can be signed by AEM Publish. To do this, AEM Publish signs the AuthnRequest using the private key, that the IDP then validates the signature using the public key. This guarantees to the IDP that AuthnRequest was initiated, and requested by AEM Publish, and not a malicious third party.
All HTTP communication between IDP and AEM Publish should be over HTTPS, and thus secure by default. However, as required, SAML assertions can be encrypted in the event extra confidentiality is required on top of that provided by HTTPS. To do this, the IDP encrypts the SAML Assertion data using the private key, and AEM Publish decrypts the SAML assertion using the private key.
Both AuthnRequest signing, and SAML assertion encryption are optional, however they are both enabled, using the SAML 2.0 authentication handler OSGi configuration property useEncryption
, meaning both or neither can be used.
Obtain the public key, private key (PKCS#8 in DER format), and certificate chain file (this may be the public key) used to sign the AuthnRequest, and encrypt the SAML assertion. The keys are typically provided by the IT organization’s security team.
$ openssl req -x509 -sha256 -days 365 -newkey rsa:4096 -keyout aem-private.key -out aem-public.crt
# Provide a password (keep in safe place), and other requested certificate information
# Convert the keys to AEM's required format
$ openssl rsa -in aem-private.key -outform der -out aem-private.der
$ openssl pkcs8 -topk8 -inform der -nocrypt -in aem-private.der -outform der -out aem-private-pkcs8.der
Upload the public key to the IDP.
openssl
method above, the public key is the aem-public.crt
file.Log in to AEM Author as an AEM Administrator, to upload the private key.
Navigate to Tools > Security > Trust Store, and select authentication-service user, and select Properties from the top action bar.
Navigate to Tools > Security > Users, and select authentication-service user, and select Properties from the top action bar.
Select the Keystore tab.
Create or open the keystore. If creating a keystore, keep the password safe.
Select Add private key from DER file, and add the private key and chain file to AEM:
openssl
method above, this is the aem-private-pkcs8.der
fileopenssl
method above, this is the aem-public.crt
fileThe newly added certificate appears above the Add certificate from CRT file section.
Select Save & Close.
Select authentication-service user, and select Activate from the top action bar.
AEM’s SAML configuration is performed via the Adobe Granite SAML 2.0 Authentication Handler OSGi configuration.
The configuration is an OSGi factory configuration, meaning a single AEM as a Cloud Service Publish service may have multiple SAML configuration’s covering discrete resources trees of the repository; this is useful for multi-site AEM deployments.
OSGi property | Required | Value format | Default value | Description | |
---|---|---|---|---|---|
Paths | path |
✔ | String array | / |
AEM paths this authentication handler is used for. |
IDP URL | idpUrl |
✔ | String | IDP URL the SAML authentication request is sent. | |
IDP certificate alias | idpCertAlias |
✔ | String | The alias of the IDP certificate found in the AEM’s Global Trust Store | |
IDP HTTP redirect | idpHttpRedirect |
✘ | Boolean | false |
Indicates if an HTTP Redirect to the IDP URL instead of sending an AuthnRequest. Set to true for IDP initiated authentication. |
IDP identifier | idpIdentifier |
✘ | String | Unique IDP Id to ensure AEM user and group uniqueness. If empty, the serviceProviderEntityId is used instead. |
|
Assertion consumer service URL | assertionConsumerServiceURL |
✘ | String | The AssertionConsumerServiceURL URL attribute in the AuthnRequest specifying where the <Response> message must be sent to AEM. |
|
SP entity Id | serviceProviderEntityId |
✔ | String | Uniquely identifies AEM to the IDP; usually the AEM host name. | |
SP encryption | useEncryption |
✘ | Boolean | true |
Indicates if the IDP encrypts SAML assertions. Requires spPrivateKeyAlias and keyStorePassword to be set. |
SP private key alias | spPrivateKeyAlias |
✘ | String | The alias of the private key in the authentication-service user’s key store. Required if useEncryption is set to true . |
|
SP key store password | keyStorePassword |
✘ | String | The password of ‘authentication-service’ user’s keys store. Required if useEncryption is set to true . |
|
Default redirect | defaultRedirectUrl |
✘ | String | / |
The default redirect URL after successful authentication. Can be relative to the AEM host (for example, /content/wknd/us/en/html ). |
User Id attribute | userIDAttribute |
✘ | String | uid |
The name of the SAML assertion attribute containing the user ID of the AEM user. Leave empty to use the Subject:NameId . |
Auto-create AEM users | createUser |
✘ | Boolean | true |
Indicates if AEM users are created on successful authentication. |
AEM user intermediate path | userIntermediatePath |
✘ | String | When creating AEM users, this value is used as the intermediate path (for example, /home/users/<userIntermediatePath>/jane@wknd.com ). Requires createUser to be set to true . |
|
AEM user attributes | synchronizeAttributes |
✘ | String array | List of SAML attribute mappings to store on the AEM user, in the format [ "saml-attribute-name=path/relative/to/user/node" ] (for example, [ "firstName=profile/givenName" ] ). See the full list of native AEM attributes. |
|
Add user to AEM groups | addGroupMemberships |
✘ | Boolean | true |
Indicates if an AEM user is automatically added to AEM user groups after successful authentication. |
AEM group membership attribute | groupMembershipAttribute |
✘ | String | groupMembership |
The name of the SAML assertion attribute containing a list of AEM user groups the user should be added to. Requires addGroupMemberships to be set to true . |
Default AEM groups | defaultGroups |
✘ | String array | A list of AEM user groups authenticated users are always added to (for example, [ "wknd-user" ] ). Requires addGroupMemberships to be set to true . |
|
NameIDPolicy Format | nameIdFormat |
✘ | String | urn:oasis:names:tc:SAML:2.0:nameid-format:transient |
The value of the NameIDPolicy format parameter to send in the AuthnRequest message. |
Store SAML response | storeSAMLResponse |
✘ | Boolean | false |
Indicates if the samlResponse value is stored on the AEM cq:User node. |
Handle logout | handleLogout |
✘ | Boolean | false |
Indicates if logout request is handled by this SAML authentication handler. Requires logoutUrl to be set. |
Logout URL | logoutUrl |
✘ | String | IDP’s URL where the SAML logout request is sent to. Required if handleLogout is set to true . |
|
Clock tolerance | clockTolerance |
✘ | Integer | 60 |
IDP and AEM (SP) clock skew tolerance when validating SAML assertions. |
Digest method | digestMethod |
✘ | String | http://www.w3.org/2001/04/xmlenc#sha256 |
The digest algorithm that the IDP uses when signing a SAML message. |
Signature method | signatureMethod |
✘ | String | http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 |
The signature algorithm that the IDP uses when signing a SAML message. |
Identity sync type | identitySyncType |
✘ | default or idp |
default |
Do not change from default for AEM as a Cloud Service. |
Service ranking | service.ranking |
✘ | Integer | 5002 |
Higher ranking configurations are preferred for the same path . |
AEM uses the following user attributes, which can be populated via the synchronizeAttributes
property in the Adobe Granite SAML 2.0 Authentication Handler OSGi configuration. Any IDP attributes can be synchronized to any AEM user property, however mapping to AEM use attribute properties (listed below) allows AEM to naturally use them.
User attribute | Relative property path from rep:User node |
---|---|
Title (for example, Mrs ) |
profile/title |
Given name (i.e. first name) | profile/givenName |
Family name (i.e. last name) | profile/familyName |
Job title | profile/jobTitle |
Email address | profile/email |
Street address | profile/street |
City | profile/city |
Postal code | profile/postalCode |
Country | profile/country |
Phone number | profile/phoneNumber |
About me | profile/aboutMe |
Create an OSGi configuration file in your project at /ui.config/src/main/content/jcr_root/wknd-examples/osgiconfig/config.publish/com.adobe.granite.auth.saml.SamlAuthenticationHandler~saml.cfg.json
and open in your IDE.
/wknd-examples/
to your /<project name>/
~
in the filename should uniquely identify this configuration, so it may be the name of the IDP, such as ...~okta.cfg.json
. The value should be alphanumeric with hyphens.Paste the following JSON into the com.adobe.granite.auth.saml.SamlAuthenticationHandler~...cfg.json
file, and update the wknd
references as needed.
{
"path": [ "/content/wknd", "/content/dam/wknd" ],
"idpCertAlias": "$[env:SAML_IDP_CERT_ALIAS;default=certalias___1652125559800]",
"idpIdentifier": "$[env:SAML_IDP_ID;default=http://www.okta.com/exk4z55r44Jz9C6am5d7]",
"idpUrl": "$[env:SAML_IDP_URL;default=https://dev-5511372.okta.com/app/dev-5511372_aemasacloudservice_1/exk4z55r44Jz9C6am5d7/sso/saml]",
"serviceProviderEntityId": "$[env:SAML_AEM_ID;default=https://publish-p123-e456.adobeaemcloud.com]",
"useEncryption": false,
"createUser": true,
"userIntermediatePath": "wknd/idp",
"synchronizeAttributes":[
"firstName=profile/givenName"
],
"addGroupMemberships": true,
"defaultGroups": [
"wknd-users"
]
}
Update the values as required by your project. See the SAML 2.0 Authentication Handler OSGi configuration glossary above for configuration property descriptions
It is recommended, but not required, to use OSGi environment variables and secrets, when values may change out of sync with the release cycle, or when the values different between similar environment types/service tiers. Default values can be set using the $[env:..;default=the-default-value]"
syntax as shown above.
OSGi configurations per environment (config.publish.dev
, config.publish.stage
, and config.publish.prod
) can be defined with specific attributes if the SAML configuration varies between environments.
When encrypting the AuthnRequest and SAML assertion, the following properties are required: useEncryption
, spPrivateKeyAlias
, and keyStorePassword
. The keyStorePassword
contains a password therefore the value must not be stored in the OSGi configuration file, but rather injected using secret configuration values
Open /ui.config/src/main/content/jcr_root/wknd-examples/osgiconfig/config.publish/com.adobe.granite.auth.saml.SamlAuthenticationHandler~saml.cfg.json
in your IDE.
Add the three properties useEncryption
, spPrivateKeyAlias
, and keyStorePassword
as shown below.
{
"path": [ "/content/wknd", "/content/dam/wknd" ],
"idpCertAlias": "$[env:SAML_IDP_CERT_ALIAS;default=certalias___1234567890]",
"idpIdentifier": "$[env:SAML_IDP_ID;default=http://www.okta.com/abcdef1235678]",
"idpUrl": "$[env:SAML_IDP_URL;default=https://dev-5511372.okta.com/app/dev-123567890_aemasacloudservice_1/abcdef1235678/sso/saml]",
"serviceProviderEntityId": "$[env:SAML_AEM_ID;default=https://publish-p123-e456.adobeaemcloud.com]",
"useEncryption": true,
"spPrivateKeyAlias": "$[env:SAML_AEM_KEYSTORE_ALIAS;default=aem-saml-encryption]",
"keyStorePassword": "$[secret:SAML_AEM_KEYSTORE_PASSWORD]",
"createUser": true,
"userIntermediatePath": "wknd/idp"
"synchronizeAttributes":[
"firstName=profile/givenName"
],
"addGroupMemberships": true,
"defaultGroups": [
"wknd-users"
]
}
The three OSGi configuration properties required for encryption are:
useEncryption
set to true
spPrivateKeyAlias
contains the keystore entry alias for the private key used by the SAML integration.keyStorePassword
contains an OSGi secret configuration variable containing the authentication-service
user keystore’s password.During the SAML authentication process, the IDP initiates a client-side HTTP POST to AEM Publish’s .../saml_login
end point. If the IDP and AEM Publish exist on different origin, AEM Publish’s Referrer Filter is configured via OSGi configuration to allow HTTP POSTs from the IDP’s origin.
Create (or edit) an OSGi configuration file in your project at /ui.config/src/main/content/jcr_root/wknd-examples/osgiconfig/config.publish/org.apache.sling.security.impl.ReferrerFilter.cfg.json
.
/wknd-examples/
to your /<project name>/
Ensure the allow.empty
value is set to true
, the allow.hosts
(or if you prefer, allow.hosts.regexp
) contains the IDP’s origin, and filter.methods
includes POST
. The OSGi configuration should be similar to:
{
"allow.empty": true,
"allow.hosts.regexp": [ ],
"allow.hosts": [
"$[env:SAML_IDP_REFERRER;default=dev-123567890.okta.com]"
],
"filter.methods": [
"POST",
],
"exclude.agents.regexp": [ ]
}
AEM Publish supports a single Referrer filter configuration, so merge the SAML configuration requirements, with any existing configurations.
OSGi configurations per environment (config.publish.dev
, config.publish.stage
, and config.publish.prod
) can be defined with specific attributes if the allow.hosts
(or allow.hosts.regex
) vary between environments.
During the SAML authentication process, the IDP initiates a client-side HTTP POST to AEM Publish’s .../saml_login
end point. If the IDP and AEM Publish exist on different hosts/domains, AEM Publish’s CRoss-Origin Resource Sharing (CORS) must be configured to allow HTTP POSTs from the IDP’s host/domain.
This HTTP POST request’s Origin
header usually has a different value than the AEM Publish host, thus requiring CORS configuration.
When testing SAML authentication on the local AEM SDK (localhost:4503
), the IDP may set the Origin
header to null
. If so, add "null"
to the alloworigin
list.
/ui.config/src/main/content/jcr_root/wknd-examples/osgiconfig/config.publish/com.adobe.granite.cors.impl.CORSPolicyImpl~saml.cfg.json
/wknd-examples/
to your project name~
in the filename should uniquely identify this configuration, so it may be the name of the IDP, such as ...CORSPolicyImpl~okta.cfg.json
. The value should be alphanumeric with hyphens.com.adobe.granite.cors.impl.CORSPolicyImpl~...cfg.json
file.{
"alloworigin": [
"$[env:SAML_IDP_ORIGIN;default=https://dev-1234567890.okta.com]",
"null"
],
"allowedpaths": [
".*/saml_login"
],
"supportedmethods": [
"POST"
]
}
OSGi configurations per environment (config.publish.dev
, config.publish.stage
, and config.publish.prod
) can be defined with specific attributes if the alloworigin
and allowedpaths
varies between environments.
After successful authentication to the IDP, the IDP will orchestrate an HTTP POST back to AEM’s registered /saml_login
end point (configured in the IDP). This HTTP POST to /saml_login
is blocked by default at Dispatcher, so it must be explicitly allowed using the following Dispatcher rule:
dispatcher/src/conf.dispatcher.d/filters/filters.any
in your IDE./saml_login
....
# Allow SAML HTTP POST to ../saml_login end points
/0190 { /type "allow" /method "POST" /url "*/saml_login" }
If URL rewriting at the Apache webserver is configured (dispatcher/src/conf.d/rewrites/rewrite.rules
), ensure that requests to the .../saml_login
end points are not accidentally mangled.
Once the SAML authentication flow creates a user in AEM Publish, the AEM user node authenticatable across the AEM Publish service tier.
This requires data synchronization and encapsulated tokens to be enabled by Adobe Support on the AEM Publish service.
Send a request to Adobe Customer Support (via AdminConsole > Support) requesting:
Data synchronization and encapsulated tokens are enabled on AEM Publish service for Program X and Environment Y.
The OSGi configurations must be committed to Git and deployed to AEM as a Cloud Service using Cloud Manager.
$ git remote -v
adobe https://git.cloudmanager.adobe.com/myOrg/myCloudManagerGit/ (fetch)
adobe https://git.cloudmanager.adobe.com/myOrg/myCloudManagerGit/ (push)
$ git add .
$ git commit -m "SAML 2.0 configurations"
$ git push adobe saml-auth:develop
Deploy the target Cloud Manager Git branch (in this example, develop
), using a Full Stack deployment pipeline.