Open Ask AI (⌘/Ctrl+I)

SSO in DX Cloud

The instructions on this page focus primarily on the required steps to integrate into our Identity Platform (IdP) based on Keycloak, which gives you access to our DX Cloud Service via SSO.

Overview

The Magnolia SSO module delegates authentication from a Magnolia instance to an OpenID Connect identity and access management application. For module configuration and guides, see the SSO module documentation.

Keycloak settings for SSO

To integrate an external IdP Directory Service for Single-Sign-On (SSO) with DX Cloud, it’s possible to setup a connection via SAML or OpenID Connect (OIDC).

Example: Azure AD Integration with Keycloak via OpenID Connect (OIDC)

As OIDC is the preferred way, the following instructions explain the basic Integration steps for Keycloak and Azure AD via OIDC authentication (OAuth2 for authorization). To integrate an existing Azure AD with Keycloak via OIDC, the following steps are required to be performed (ordered). All Azure Actions are performed via the Microsoft Azure portal.

Basic setup

Azure

  1. Create an Enterprise Application and copy the ApplicationID (ie. 1978A428-FY151-444B-AA32-GB18DCB7F482)

    Create Enterprise Application in Azure

    Copy Application ID

  2. Navigate to App Registrations and find your recently created application and open it.

    App Registrations in Azure

  3. In Manage > Certificates & Secrets, create a Client Secret and copy down the new Secret Value (Secret ID is not required).

    Create App Registration secret

    Copy App Registration secret value

  4. In App Registrations on the newly created app, open Overview > Endpoints and note the OpenID Metadata Document URL of the new Application.

    Well-known OpenID document endpoint

  5. Finally, in the Authentication section of the App Registration, add the Magnolia Keycloak URLs as valid redirect URIs (Platform Web). Please enter them in the following format (replace <customer> with your customer shortname):

    • EMEA: https://id.eu-central.magnolia-platform.com/realms/<customer>/broker/oidc/endpoint
    • APAC: https://id.ap-southeast.magnolia-platform.asia/realms/<customer>/broker/oidc/endpoint
    • US: https://id.us-east.magnolia-platform.us/realms/<customer>/broker/oidc/endpoint

    Azure Authentication redirect URIs configuration

Keycloak

  1. In the <customer> realm under Identity Providers, create a new IdP Integration using the information received before.

    • Client ID (Application ID)
    • Client Secret (Application Secret Secret Value)
    • Well-Known URL (OpenID Metadata Document URL including all necessary information)

    Fetch OIDC metadata to “autofill” basic Information

    Create Keycloak IdP step 1

    Create Keycloak IdP step 2

  2. For the ClientID, enter the Azure App’s ApplicationID.

  3. For the ClientSecret, enter Azure App’s Secret Value.

    Enter Client ID and Secret in Keycloak

  4. Perform a basic Test on Realm ID.

  5. Prepare RoleMapping (Map OpenID “roles” to existing keycloak roles).

App mapping

Azure

IdP naming and structure for groups, users, roles, and assignments are flexible, so it’s important for SSO to align and match on certain role assignments to control granular access privileges.

Aligning Claim-Value with the Group name in both Keycloak and Azure AD helps to avoid any confusion or misconfiguration.

Use the DX Cloud groups to prepare an IdP for onboarding with DX Cloud Keycloak.

  1. In App Registrations of your app, create your desired AppRole (ie. Magnolia, Cockpit, Rancher) to be delivered as an OIDC Claim Value (Value = Claim Value in Keycloak).

    Create AppRole in Azure

  2. In (Enterprise) Application, map the AppRole on a User and/or Group Level in Azure AD.

    Map AppRole to users/groups

authorizationGenerators:
  - name: groupsAuthorization
    groups:
      targetProperty: magnolia-roles <1>
...

targetProperty maps to the groups in your IDP for the SSO module. See SSO module: Configuring via yaml for more details.

Keycloak

  1. In <customer> > Identity Providers > Mappers, map Claim Value (set before in Azure as AppRole) of the OIDC Token Claim roles to the existing Role in Keycloak IdP.

    Keycloak role mapping

Repeat the steps of App mapping per Application you want to integrate. For more information on installing and configuring the SSO Module and securing Magnolia Admincentral Access, check out the SSO module.

Further reading

Set sameSiteCookies to lax

In your Tomcat configuration, make sure that the CookieProcessor component does not have the sameSiteCookies property set to strict. Instead, set the property to Lax:

Tomcat

<CookieProcessor sameSiteCookies="Lax" /> <1>

sameSiteCookies set to Lax instead of strict.

Modify values.yml

To modify sameSiteCookies, update your values.yml file.

values.yml

magnoliaAuthor:
  sameSiteCookies: Lax <1>

sameSiteCookies set to Lax instead of strict.

Integrate SSO in DX Cloud PaaS

This section covers Single Sign-On (SSO) in the DX Cloud PaaS environment with Azure Active Directory (AD). Work through the sections in order: Magnolia prerequisites, Azure AD, Magnolia SSO config for Azure (including Helm secrets), then debugging and troubleshooting if needed. When each instance uses the same SSO identity provider, users can log in once and access multiple instances without re-authentication.

Magnolia prerequisites

Configure JAAS

Configure JAAS in your webapp bundle.

WEB-INF/config/jaas.config

magnolia {
  info.magnolia.jaas.sp.jcr.JCRAuthenticationModule requisite;
  info.magnolia.jaas.sp.jcr.JCRAuthorizationModule required;
};

sso-authentication {
  info.magnolia.sso.jaas.SsoAuthenticationModule requisite;
  info.magnolia.jaas.sp.jcr.JCRAuthorizationModule required;
};

Add the SSO config YAML

Add the SSO config yaml in the right place.

  • The default location is: <light-modules>/magnolia-sso/config.yaml.
  • Ensure that you set the magnolia.sso.config property to an existing value.

Set sameSiteCookies to Lax

Your sameSiteCookies setting is strict by default in PaaS. You must set it to Lax when using SSO (<CookieProcessor sameSiteCookies="Lax" />). See Modify values.yml for more details.

values.yaml

magnoliaAuthor:
  sameSiteCookies: Lax

Configure Azure AD

When integrating with an identity and access management (IAM) service such as Azure, configure your Identity Provider (IdP) as follows.

Redirect URLs

Configure redirect URLs.

Azure SSO redirect URIs configuration

Assign groups to your client

Assign groups to your client.

Azure SSO users and groups

Prepare group mappings

Example mapping: Azure display name | Magnolia roles | Magnolia groups | Azure Group Object ID → magnolia.superuser | superuser | publisher | 6c1c2684-78aa-4fd5-a976-abc.

Given that mapping, your configuration is as follows:

- name: groupsAuthorization
    groups:
      mappings:
        - name: 6c1c2684-78aa-4fd5-a976-abc #magnolia.superuser
          targetRoles:
            - superuser
          targetGroups:
            - publisher

Choose group authorization

Pick one of the following approaches.

Option 1: Groups claim in the access token

Allow groups claim in the access token.

  1. Configure groups to be returned in the access token.

    See the SSO module prerequisites for the Azure groups-claim configuration screenshot and steps.

  2. Configure authorizationGenerators in your SSO config.yaml. This defines the groups and roles for your client. They are needed for groups and fixed role generators. If you want a custom generator, follow the steps here.

callbackUrl: /.auth
postLogoutRedirectUri: http://localhost:8080
authorizationGenerators:
  - name: groupsAuthorization
    groups:
      targetProperty: groups
      mappings:
        - name: superusers
          targetGroups:
            - publishers
          targetRoles:
            - superuser

Option 2: Custom group authentication

Include this custom group authentication.

Magnolia SSO config for Azure

Azure OIDC client settings

Check your existing SSO config and include oidc.type: azure and oidc.azure.tenant: <azureTenantId>.

Example SSO config

callbackUrl: /.auth
authorizationGenerators:
  - name: fixedRoleAuthorization
    fixed:
      targetRoles:
        - superuser

clients:
  oidc.type: azure <1>
  oidc.azure.tenant: <azureTenantId> <2>
  oidc.id: !env ${SSO_OIDCID}
  oidc.secret: !env ${SSO_OIDCSECRET}
  oidc.clientAuthenticationMethod: client_secret_post
  oidc.scope: openid profile email
  oidc.discoveryUri: https://login.microsoftonline.com/<azureTenantId>/v2.0/.well-known/openid-configuration
  oidc.preferredJwsAlgorithm: RS256
  oidc.authorizationGenerators: fixedRoleAuthorization
userFieldMappings:
  name: name
  removeEmailDomainFromUserName: true
  removeSpecialCharactersFromUserName: false
  fullName: name
  email: email
  language: locale

Add oidc.type: azure.

Add oidc.azure.tenant: <azureTenantId>.

Hide client credentials in Helm values

If you want to hide client credentials from the configuration file, enable them as catalinaExtraEnv for both author and public instances in your Helm values.yaml file.

magnoliaAuthor:
  env:
    - name: SSO_OIDCID
      value: {{ .Env.SSO_OIDCID | quote }}
    - name: SSO_OIDCSECRET
      value: {{ .Env.SSO_OIDCSECRET | quote }}
    - name: SSO_POST_LOGOUT_URI
      value: {{ .Env.SSO_POST_LOGOUT_URI | quote }}
    - name: SSO_CALLBACK_URL
      value: {{ .Env.SSO_CALLBACK_URL | quote }}
  catalinaExtraEnv:
    magnolia.yaml.envsubst: "true"

Debugging

Enable DEBUG in Log Tools

Log in with a Magnolia account and navigate to the Log Tools app. Set these loggers to DEBUG. These settings reset upon pod restart.

  • info.magnolia.sso: DEBUG
  • org.pac4j: DEBUG
  • com.nimbusds: DEBUG

Persist DEBUG in values.yaml

Set the same loggers in your values.yaml and redeploy.

magnoliaAuthor:
  logging:
    loggers:
      - name: info.magnolia.sso
        level: DEBUG
      - name: org.pac4j
        level: DEBUG
      - name: com.nimbusds
        level: DEBUG

Change loggers via ConfigMap (without redeploy)

Alternatively, edit the configmap (for example, dev-configmap-author) without redeploying:

  1. Edit YAML in ConfigMaps. Editing YAML in ConfigMaps

  2. Scroll down to log4j.xml and paste the loggers. Paste loggers in log4j.xml

  3. Restart your pod.

Troubleshooting

Make sure these configs are available and in the correct order.

Login handlers

/server/filters/login/loginHandlers from SSO 4.x (class: info.magnolia.cms.security.auth.login.LoginFilter):

  • /server/filters/login/loginHandlers/Basic/class=info.magnolia.cms.security.auth.login.*BasicLogin*
  • /server/filters/login/loginHandlers/sso/class=info.magnolia.sso.auth.login.*SsoLoginHandler*
  • /server/filters/login/loginHandlers/Form/class=info.magnolia.cms.security.auth.login.*FormLogin*

Login handlers configuration

Logout handlers

/server/filters/logout from SSO 4.x (class: info.magnolia.cms.security.LogoutFilter):

  • /server/filters/logout/handlers/extends=../../login/loginHandlers

Logout handlers configuration

Security callback clientCallbacks

/server/filters/securityCallback/clientCallbacks:

  • /server/filters/securityCallback/clientCallbacks/ssoFallback/class=info.magnolia.cms.security.auth.callback.*FormClientCallback*
  • /server/filters/securityCallback/clientCallbacks/ssoLocationFragmentRedirect/class=info.magnolia.sso.*LocationFragmentRedirectClientCallback*
  • /server/filters/securityCallback/clientCallbacks/sso/class=info.magnolia.sso.*UserInitiatedRedirectClientCallback*
  • /server/filters/securityCallback/clientCallbacks/form/class=info.magnolia.cms.security.auth.callback.*FormClientCallback*

Security callback clientCallbacks

SSOCallbackServlet

Order of /server/filters/servlets/SSOCallbackServlet:

  • /server/filters/servlets/SSOCallbackServlet/servletClass=info.magnolia.sso.SsoCallbackServlet
  • /server/filters/servlets/SSOCallbackServlet/mappings/-.auth/pattern=/.auth

SSOCallbackServlet configuration

User managers

/server/security/userManagers:

  • /server/security/userManagers/system/class=info.magnolia.cms.security.*SystemUserManager*
  • /server/security/userManagers/admin/class=info.magnolia.cms.security.*MgnlUserManager*
  • /server/security/userManagers/sso-authentication/class=info.magnolia.sso.*SsoUserManager*

userManagers configuration

Groovy check script

Use the Groovy script below to check the values.

import info.magnolia.jcr.util.NodeUtil

session = MgnlContext.getJCRSession("config");
println("--callbacks")
clientCallbacks = session.getNode("/server/filters/securityCallback/clientCallbacks");
print(info.magnolia.cms.util.DumperUtil.dump(clientCallbacks, 3));
println("--login")
login = session.getNode("/server/filters/login/loginHandlers");
print(info.magnolia.cms.util.DumperUtil.dump(login, 3));

println("--logout")
logout = session.getNode("/server/filters/logout");
print(info.magnolia.cms.util.DumperUtil.dump(logout, 3));

println("--callbackServlet")
callbackServlet = session.getNode("/server/filters/servlets/SSOCallbackServlet");
print(info.magnolia.cms.util.DumperUtil.dump(callbackServlet, 3));

println("--userManagers")
userManagers = session.getNode("/server/security/userManagers");
print(info.magnolia.cms.util.DumperUtil.dump(userManagers, 3));

println("--sso version")
session = MgnlContext.getJCRSession("config");
ssoVersion = session.getNode("/modules/sso");
print(info.magnolia.cms.util.DumperUtil.dump(ssoVersion, 3));

Multisite site definition

For public instances, SSO can be triggered on the main domain because of a missing multisite configuration. Define your site definition as follows.

domains:
  example:
    name: example.com
  example-dev:
    name: test-dev.public.magnolia-platform.io

mappings:
  website:
    URIPrefix: ''
    handlePrefix: /example
    repository: website

Logout example

Logout example without postLogoutRedirectUri in your SSO config.yaml.

Successful logout

Successful logout

Console log for a successful logout

2025-06-12 13:12:50,315 DEBUG org.pac4j.core.engine.DefaultLogoutLogic          : redirectUrl: http://localhost:8585/.magnolia/admincentral
2025-06-12 13:12:50,315 DEBUG org.pac4j.core.engine.DefaultLogoutLogic          : Performing application logout
2025-06-12 13:12:50,315 DEBUG org.pac4j.jee.context.session.JEESessionStore     : createSession: false, retrieved session: org.apache.catalina.session.StandardSessionFacade@14f1e877
2025-06-12 13:12:50,315 DEBUG org.pac4j.jee.context.session.JEESessionStore     : Get sessionId: F00762317C5AF70DFFA29B051B8B3046
2025-06-12 13:12:50,315 DEBUG org.pac4j.core.profile.ProfileManager             : Removing profiles from session
2025-06-12 13:12:50,315 DEBUG org.pac4j.jee.context.session.JEESessionStore     : createSession: true, retrieved session: org.apache.catalina.session.StandardSessionFacade@14f1e877
2025-06-12 13:12:50,315 DEBUG org.pac4j.jee.context.session.JEESessionStore     : Set key: pac4jUserProfiles for value: {}
2025-06-12 13:12:50,315 DEBUG org.pac4j.core.profile.ProfileManager             : Removing profiles from request
2025-06-12 13:12:50,315 DEBUG org.pac4j.jee.context.session.JEESessionStore     : createSession: false, retrieved session: org.apache.catalina.session.StandardSessionFacade@14f1e877
2025-06-12 13:12:50,315 DEBUG org.pac4j.jee.context.session.JEESessionStore     : Invalidate session: org.apache.catalina.session.StandardSessionFacade@14f1e877
2025-06-12 13:12:50,315 DEBUG org.pac4j.core.engine.DefaultLogoutLogic          : Performing central logout
2025-06-12 13:12:50,316 DEBUG org.pac4j.core.engine.DefaultLogoutLogic          : Profile: #AzureAdProfile# | id: QZwkH..IVRKjMSTcU0O4 | attributes: {sub=QZwkHnvFW...IVRKjMSTcU0O4, xms_pl=en, iss=https://login.microsoftonline.com/123-52a7-4eed-a51d-/v2.0, oid=6ef8238b-2e7b-45f7-afbe-18cb3b15ab28, preferred_username=123@outlook.com, acrs=[p1], tid=123-52a7-4eed-a51d-0910bfd660ff, sid=005d6a29-4dac-dbf6-0525-3f8bea6576a7, login_hint=O.CiQwMDAwMDAwMC0wMDAwLTAwMDAtMDRkYy1mYWM3NDg2MDcwZjcSJDkxODgwNDBkLTZjNjctNGM1Yi1iMTEyLTM2YTMwNGI2NmRhZBocZG9uZ2FuaGhhbm9pMTExMEBvdXRsb29rLmNvbSAp, ctry=VN, auth_time=Thu Jun 12 13:12:44 ICT 2025, exp=Thu Jun 12 14:12:44 ICT 2025, ipaddr=45.126.93.146, iat=Thu Jun 12 13:07:44 ICT 2025, email=123@outlook.com, mgnlGroups=[swissre-admin], ver=2.0, xms_tpl=en, id_token=A, tenant_ctry=VN, token_expiration_advance=0, aud=[7b3a95d7-1573-482d-a51f-6eba91110d0a], tenant_region_scope=AS, nbf=Thu Jun 12 13:07:44 ICT 2025, idp=https://sts.windows.net/9188040d-6c67-4c5b-b112-36a304b66dad/, rh=1.AXAAktsmnKd..RDQpwAHlwAA., name=Minh Anh, expiration=1749713137608, family_name=Anh, acct=0} | authenticationAttributes: {} | roles: [rest-admin, swissre-admin] | permissions: [] | isRemembered: false | clientName: AzureAd2Client | linkedId: null |
2025-06-12 13:12:50,316 DEBUG org.pac4j.core.client.Clients                     : Found client: #AzureAd2Client# | name: AzureAd2Client | callbackUrl: /.auth | callbackUrlResolver: org.pac4j.core.http.callback.NoParameterCallbackUrlResolver@d260613 | ajaxRequestResolver: org.pac4j.core.http.ajax.DefaultAjaxRequestResolver@64bbd2c9 | redirectionActionBuilder: org.pac4j.oidc.redirect.OidcRedirectionActionBuilder@28c07808 | credentialsExtractor: org.pac4j.oidc.credentials.extractor.OidcExtractor@200c6fc | authenticator: org.pac4j.oidc.credentials.authenticator.OidcAuthenticator@3fab5e31 | profileCreator: org.pac4j.oidc.profile.azuread.AzureAdProfileCreator@53cee97 | logoutActionBuilder: org.pac4j.oidc.logout.OidcLogoutActionBuilder@2e17c600 | authorizationGenerators: [info.magnolia.sso.oidc.FixedRoleAuthorizationGenerator@2aa4c218] | configuration: #AzureAd2OidcConfiguration# | clientId: 7b3a95d7-1573-482d-a51f-6eba91110d0a | secret: [protected] | discoveryURI: https://login.microsoftonline.com/123-52a7-4eed-a51d-0910bfd660ff/v2.0/.well-known/openid-configuration | scope: openid profile email | customParams: {} | clientAuthenticationMethod: client_secret_post | useNonce: false | preferredJwsAlgorithm: RS256 | maxAge: null | maxClockSkew: 30 | connectTimeout: 500 | readTimeout: 5000 | resourceRetriever: org.pac4j.oidc.client.azuread.AzureAdResourceRetriever@4f9b4bab | responseType: code | responseMode: null | logoutUrl: null | withState: true | stateGenerator: org.pac4j.core.util.generator.RandomValueGenerator@2db15c99 | logoutHandler: #DefaultLogoutHandler# | store: #GuavaStore# | size: 10000 | timeout: 30 | timeUnit: MINUTES | | destroySession: false | | tokenValidator: org.pac4j.oidc.profile.azuread.AzureAdTokenValidator@3d86b8e9 | mappedClaims: {} | allowUnsignedIdTokens: false | SSLFactory: null | privateKeyJWTClientAuthnMethodConfig: null | callUserInfoEndpoint: true | | for name: AzureAd2Client
2025-06-12 13:12:50,316 DEBUG org.pac4j.core.engine.DefaultLogoutLogic          : Logout action: Optional[#FoundAction# | code: 302 | location: https://login.microsoftonline.com/123-52a7-4eed-a51d-/oauth2/v2.0/logout?post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A8585%2F.magnolia%2Fadmincentral&id_token_hint=eyJ0eXA |]
2025-06-12 13:12:50,316 DEBUG info.magnolia.sso.auth.login.SsoLoginHandler      : Logout from IdP executed, session should have been removed from the server.
2025-06-12 13:12:50,316 INFO  info.magnolia.audit.AuditLoggingManager           : [Audit] logout, Minh Anh
2025-06-12 13:12:50,316 DEBUG info.magnolia.sso.auth.login.SsoLoginHandler      : Magnolia user session has been removed.
2025-06-12 13:12:50,416 DEBUG agnolia.sso.LocationFragmentRedirectClientCallback: Remember location fragment feature is disabled.
2025-06-12 13:12:50,416 DEBUG info.magnolia.sso.pac4j.SecFetchMode              : Sec-Fetch-Mode: no-cors
2025-06-12 13:12:57,035 DEBUG agnolia.sso.LocationFragmentRedirectClientCallback: Remember location fragment feature is disabled.
2025-06-12 13:12:57,035 DEBUG info.magnolia.sso.pac4j.SecFetchMode              : Sec-Fetch-Mode: navigate
2025-06-12 13:12:57,035 DEBUG o.magnolia.sso.UserInitiatedRedirectClientCallback: Attempting to redirect to: http://localhost:8585/.magnolia/admincentral
2025-06-12 13:12:57,035 DEBUG o.magnolia.sso.UserInitiatedRedirectClientCallback: Redirect successful to: http://localhost:8585/.magnolia/admincentral
2025-06-12 13:12:57,038 DEBUG info.magnolia.sso.auth.login.SsoLoginHandler      : SSO login handler.
2025-06-12 13:12:57,038 DEBUG org.pac4j.core.engine.DefaultSecurityLogic        : === SECURITY ===
2025-06-12 13:12:57,038 DEBUG org.pac4j.core.engine.DefaultSecurityLogic        : url: http://localhost:8585/.magnolia/admincentral