To implement SAML (Security Assertion Markup Language) on the publish environment in Adobe Experience Manager (AEM), following steps are required.

  1. Configure SAML Authentication Handler:
    • Update the SAML authentication handler configuration in the customer code.
  2. Configure Identity Provider (IDP):
    • Set up the IDP (e.g., Okta, Azure AD, or ADFS) by providing the appropriate metadata for SAML integration.
    • Verify that the IDP configuration matches the settings of the SAML authentication handler using SP(AEM) metadata.
  3. Establish a Keystore for the Authentication Service User:
    • Create and configure a keystore for the AEM authentication service user.
  4. Set up Truststore:
    • Configure a truststore that encompasses the certificate of the identity provider.
  5. Replicate Authentication Service User and Truststore:
    • Replicate both the authentication-service user and the truststore from the author environment to the publish environment.
    • This replication can be achieved through CRXDE or by utilizing tree replication within the AEM user interface (UI).
    • To initiate the replication, navigate to the UI’s Tools > Deployment > Distribution section.
  6. Set up Referrer Filter:
    • Configure the referrer filter to permit requests originating from the domain or specific URLs of the IDP.
    • This filter guarantees that only legitimate SAML POST requests are accepted by AEM.
  7. Configure Dispatcher Filter:
    • Modify the configuration of the Dispatcher filter to enable the transmission of SAML POST requests from the IDP back to AEM.
  8. Allow CORS
    • Allow IDP domain in the AEM CORS OSGI configuration
  9. Enable Encapsulated Token and User Synchronization:
    • To enable SAML and achieve stateless authentication with session maintenance across multiple publishers, it is essential to activate encapsulated tokens (encapsulated token). User synchronisation is a mandatory step before enabling encapsulated token to ensure that users are synchronised across all publishers. Neglecting user synchronisation may lead to authentication problems. It is recommended to maintain a short-lived sticky session to facilitate login and synchronisation of the encap token with other publishers.

Please note, The intension here is to explain the concept and provide a summary of steps required to setup SAML . If you want to understand how to’s for each step read adobe documentation.

Troubleshooting

Problem :
Instead of receiving the expected 302 response for the POST request to “saml_login,” we are receiving a 204 response for unknown reasons.

Solution:
The problem is with the CORS (Cross-Origin Resource Sharing) setup. make sure com.adobe.granite.cors.impl.CORSPolicyImpl factory configuration is properly configured and active.

CORS Policy:
Enable the CORS policy to allow Cross-Origin POST Request from

{
  "alloworigin":[
    " https://adb2c.login.com"
  ]
}

Problem :
After logging into AEM, the user is not being assigned to the groups received from the Identity Provider (IDP). Instead, they are only assigned to the default group.

Solution:
To troubleshoot the issue, examine the SAML response and verify the attribute name specified in the groupMembershipAttribute attribute of the SAML OSGi configuration. Compare this attribute name with the corresponding attribute name in the received SAML response.

Verify the value of the groupName attribute in the SAML response. Ensure that the corresponding groups already exist in AEM with the same name. After successful login, the user will be assigned to these groups. The Saml:attributevalue should contain only the name of the group, as shown below.

How to Obtain/Verify SAML Assertion/Response?
For troubleshooting purposes, it can be beneficial to compare the SAML response/assertion received from the client’s Identity Provider (IDP) with the SSO circle.
This can be achieved by utilising the SAML Tracer plugin available in Google Chrome.

Problem :
Experiencing a login loop where you are not redirected back to AEM after logging in.

Solution:
The URL to which the Identity Provider (IDP) should redirect after successful authentication is not properly configured. The correct configuration should specify the URL as “http://host:port/saml_login” in SAML authentication handler OSGI configuration.

Problem :
After login, redirect to the URL of the original request.

Solution:
The redirection to the original request URL can be set up by utilising the defaultRedirectUrl configuration in the SAML authentication handler. The browser employs the saml_request_path cookie to redirect to the initial URL. The actual requested URL is included as the redirectURL in the request sent to the IDP, and it is subsequently returned to AEM. The /saml_login servlet (presumably) reads this information and redirects to the appropriate request location.

Problem :
403 forbidden

Solution:
The issue arises from the IDP response lacking proper configuration of the referrer. Allowing an empty referrer configuration can temporarily resolve the problem, but it poses a security risk and should never be implemented in a production environment. Essentially, the problem stems from the IDP failing to include the referrer value in the response. If this is indeed the issue, it will be evident in the error logs with a “forbidden” message.

Problem :
Encountering a 404 No Resource Found error

Upon reaching the Identity Provider (IdP) login screen, logging in with user credentials leads to a 404 error code and an accompanying error stack trace on the welcome page.

Solution:
The occurrence of this error suggests that the imported user does not possess the necessary permissions to access the requested resource.

Sample Test IDP account to Setup SAML
SSO Circle offers a sample Identity Provider (IDP) that can be utilised for local setups and gaining a better understanding of SAML (Security Assertion Markup Language) functionality. To explore and set up SAML locally, you can visit the SSO Circle website at https://www.ssocircle.com/en/.

SAMPLE SP Metadata

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<EntityDescriptor entityID="AEMLOCALSPENTITYID"
	xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
	<SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
		<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
		<AssertionConsumerService index="0" isDefault="true" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:4502/content/saml_login"/>
	</SPSSODescriptor>
</EntityDescriptor>

Sample SAML assertion which works with AEM.
Sample SAML assertion which works with AEM.

<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                ID="s202d5a0bac9dc78dd1e2fefd49616418503831e83"
                Version="2.0"
                IssueInstant="2020-06-19T06:51:52Z"
                Destination="http://localhost:4502/content/saml_login"
                >
    <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://idp.test.com</saml:Issuer>
    <samlp:Status xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
        <samlp:StatusCode xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                          Value="urn:oasis:names:tc:SAML:2.0:status:Success"
                          />
    </samlp:Status>
    <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                    ID="s2d33e7967add1b7e11620a822fba874bc14a0bdfa"
                    IssueInstant="2020-06-19T06:51:52Z"
                    Version="2.0"
                    >
        <saml:Issuer>https://idp.test.com</saml:Issuer>
        <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:SignedInfo>
                <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
                <ds:Reference URI="#s2d33e7967add1b7e11620a822fba874bc14a0bdfa">
                    <ds:Transforms>
                        <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                        <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                    </ds:Transforms>
                    <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                    <ds:DigestValue>OlhSkQm4II3T+qAVANdEvlqDAmw=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>
HgOTH/Qm5bNBdnJsA27HU6UmJ/j5iaAllzAolsd3tMnlRIhpSdiCFmwudDB0kFYoEKuF7hY8orte
hwt7Kw/iv0teGC3+DmzC3bdD+tjELuiheSLE0oXXB9d0Gfl7toNEYQnpEJaiLz/VYnhTCNS9JFCb
HhG+MpeqYWcPg2cGFXGb2p+FXhoOFBbjSailQCBeo6WINivEQ0MuTx82R7Uay0k1jXvBCHK18Iyo
eu4gXsr1LG2jIo1Zv4VF8rg1oHkPPX4gwfm+fQyqSuBSf38qExm140Ns7tzylcyeH6grnPCPmGNP
A/a9fvTmw4S6xiPGCpwplVeq0ZCIuhmo8b3yww==
</ds:SignatureValue>
            <ds:KeyInfo>
                <ds:X509Data>
                    <ds:X509Certificate>
MIIEYzCCAkugAwIBAgIDIAZmMA0GCSqGSIb3DQEBCwUAMC4xCzAJBgNVBAYTAkRFMRIwEAYDVQQK
DAlTU09DaXJjbGUxCzAJBgNVBAMMAkNBMB4XDTE2MDgwMzE1MDMyM1oXDTI2MDMwNDE1MDMyM1ow
PTELMAkGA1UEBhMCREUxEjAQBgNVBAoTCVNTT0NpcmNsZTEaMBgGA1UEAxMRaWRwLnNzb2NpcmNs
ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCAwWJyOYhYmWZF2TJvm1VyZccs
3ZJ0TsNcoazr2pTWcY8WTRbIV9d06zYjngvWibyiylewGXcYONB106ZNUdNgrmFd5194Wsyx6bPv
njZEERny9LOfuwQaqDYeKhI6c+veXApnOfsY26u9Lqb9sga9JnCkUGRaoVrAVM3yfghv/Cg/QEg+
I6SVES75tKdcLDTt/FwmAYDEBV8l52bcMDNF+JWtAuetI9/dWCBe9VTCasAr2Fxw1ZYTAiqGI9sW
4kWS2ApedbqsgH3qqMlPA7tg9iKy8Yw/deEn0qQIx8GlVnQFpDgzG9k+jwBoebAYfGvMcO/BDXD2
pbWTN+DvbURlAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu
ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQhAmCewE7aonAvyJfjImCRZDtccTAfBgNVHSME
GDAWgBTA1nEA+0za6ppLItkOX5yEp8cQaTANBgkqhkiG9w0BAQsFAAOCAgEAAhC5/WsF9ztJHgo+
x9KV9bqVS0MmsgpG26yOAqFYwOSPmUuYmJmHgmKGjKrj1fdCINtzcBHFFBC1maGJ33lMk2bM2THx
22/O93f4RFnFab7t23jRFcF0amQUOsDvltfJw7XCal8JdgPUg6TNC4Fy9XYv0OAHc3oDp3vl1Yj8
/1qBg6Rc39kehmD5v8SKYmpE7yFKxDF1ol9DKDG/LvClSvnuVP0b4BWdBAA9aJSFtdNGgEvpEUqG
kJ1osLVqCMvSYsUtHmapaX3hiM9RbX38jsSgsl44Rar5Ioc7KXOOZFGfEKyyUqucYpjWCOXJELAV
Azp7XTvA2q55u31hO0w8Yx4uEQKlmxDuZmxpMz4EWARyjHSAuDKEW1RJvUr6+5uA9qeOKxLiKN1j
o6eWAcl6Wr9MreXR9kFpS6kHllfdVSrJES4ST0uh1Jp4EYgmiyMmFCbUpKXifpsNWCLDenE3hllF
0+q3wIdu+4P82RIM71n7qVgnDnK29wnLhHDat9rkC62CIbonpkVYmnReX0jze+7twRanJOMCJ+lF
g16BDvBcG8u0n/wIDkHHitBI7bU1k6c6DydLQ+69h8SCo6sO9YuD+/3xAGKad4ImZ6vTwlB4zDCp
u6YgQWocWRXE+VkOb+RBfvP755PUaLfL63AFVlpOnEpIio5++UjNJRuPuAA=
</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </ds:Signature>
        <saml:Subject>
            <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
                         NameQualifier="https://idp.test.com"
                         SPNameQualifier="AEMLOCALSPENTITYID"
                         >testa</saml:NameID>
            <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml:SubjectConfirmationData NotOnOrAfter="2020-06-19T07:01:52Z"
                                              Recipient="http://localhost:4502/content/saml_login"
                                              />
            </saml:SubjectConfirmation>
        </saml:Subject>
        <saml:Conditions NotBefore="2020-06-19T06:41:52Z"
                         NotOnOrAfter="2020-06-19T07:01:52Z"
                         >
            <saml:AudienceRestriction>
                <saml:Audience>AEMLOCALSPENTITYID</saml:Audience>
            </saml:AudienceRestriction>
        </saml:Conditions>
        <saml:AuthnStatement AuthnInstant="2020-06-19T06:25:22Z"
                             SessionIndex="s2758177fc29ff0d1ab874fa1183129feb7e09a001"
                             >
            <saml:AuthnContext>
                <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
            </saml:AuthnContext>
        </saml:AuthnStatement>
        <saml:AttributeStatement>
            <saml:Attribute Name="EmailAddress">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >testmul1@gmail.com</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="UserID">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >testa</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="FirstName">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >test</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="LastName">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                     xsi:type="xs:string"
                                     >test</saml:AttributeValue>
            </saml:Attribute>
        </saml:AttributeStatement>
    </saml:Assertion>
</samlp:Response>

Logger Configuration
https://experienceleague.adobe.com/docs/experience-manager-65/administering/security/saml-2-0-authenticationhandler.html#configure-a-logger-for-saml

Dispatcher/CDN Configurations:
Include the following rule in the “/filter” section of the dispatcher website farm configuration.

/0100 {  /method "POST" /url "*/saml_login" }

Apache Sling Referrer Filter:
Allow ADB2c/IDP tenant url in referrer filter.

{
  "allow.hosts":[
    "https://adb2c.login.com:443"
  ]
}