Introduction

This article is about integration of a Magnolia CMS backend (AdminCentral) login with Keycloak. For this purpose we will use the SSO Connector module, which is currently in “incubator” status at Magnolia and available for licensed customers. Because of high demand, the module will be “productized” in the future, which means better code, documentation and also regular maintenance and support. In the past, there has sometimes been slight confusion about the role that Keycloak plays for the SSO Connector module, most likely because the “official” Magnolia documentation for the SSO Connector says that you need to introduce a library dependency to Keycloak to make it work. To clarify this I can say that this is wrong, the SSO Connector module is based on the OAuth / OpenID Connect protocols and has zero module dependencies on Keycloak.

About Keycloak

Citing Wikipedia:

Keycloak is an open source software product to allow single sign-on with Identity Management and Access Management aimed at modern applications and services.

In other words Keycloak is a server side product that can manage users, handle authentication and also integrate other user stores like LDAP or social service based login providers. There are many other comparable solutions available like Okta, Auth0, Airlock to mention a few. Also cloud based services like Google, Github, LinkedIn, AWS Cognito or Microsoft Azure AD support the OAuth / OpenID Connect protocols to integrate their user management solutions with solutions like the Magnolia SSO Connector module. What makes Keycloak especially attractive is the complete feature set and the fact that it is Open Source software and under active development. You can also download it and use it for local development and testing or you can use it to integrate other technologies like SAML that you can not or don’t want to support directly with your own software solution.

The SSO Connector module

In this article we will enable login to Magnolia AdminCentral with users stored in Keycloak while preserving local and LDAP logins.

Versions used

As Magnolia bundle I used magnolia-dx-core-demo-webapp version 6.1.4 and the SSO Connector bundle version 2.6.3-SNAPSHOT (my latest development version). You can also use a different type of Magnolia instance as long as the SSO Connector module is available. Keycloak 8.0.0 was used at the time of writing.

Prepare your Magnolia CMS project

Usually Magnolia projects are based on Maven. For using the SSO Connector module, we need to apply a few settings to the project.

First edit the configuration file for JAAS, in the “Webapp” of your project located under the path

/src/main/webapp/WEB-INF/config/jaas.config.

Add this section to the bottom of the file:

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

See jaas.config example file.

Also add the Maven module dependency for the SSO Connector module:

  <dependency>
    <groupId>info.magnolia.connector.sso</groupId>
    <artifactId>magnolia-sso-connector</artifactId>
    <version>${ssoConnectorVersion}</version>
  </dependency>

Rebuild the Magnolia project and start it.

General Keycloak preparation

Download the Keycloak standalone server distribution and extract it to a directory of choice. You can start the server from the /bin directory and the standalone.sh script.

Hint: Keycloak by default starts on port 8080. IF you want to avoid port collisions with other applications (like Magnolia) you can start Keycloak with

sh ./standalone.sh -Djboss.socket.binding.port-offset=100

By using the command above, Keycloak is running on port 8180 (8080 + 100).

In a fresh Keycloak installation you have to create an admin user first. To achieve this, go to http://localhost:8180/auth/ and use the interface to create your admin user account.

Login in to Keycloak and create a new realm called “Magnolia AdminCentral” (navigate to the top left near “Master” to find the button).

Under Clients click on the create button to create a new client for AdminCentral.

Give a meaningful name for the Client ID (you will need it later) and after saving, Adjust the client’s Access Type to confidential.

Add a redirect URI that is matching your Magnolia instance address:

Warning: The example redirect URI with the wildcard shown above should never be used in productive environments! Always specify the URIs there as explicit as possible!

You can also specify multiple redirect URIs which is very handy for testing purposes. Save the setup and select the Credentials tab. There you can find the Secret value that you need to use later in the SSO Connector configuration.

As we want to log in to the Magnolia backend, it makes sense to deliver group information together with the user information to Magnolia during the login process. This way Magnolia can apply permissions to a specific user account (usually Magnolia AdminCentral is used by different types of users that need different permissions, like editors, publishers, …).

Keycloak just delivers group names - these groups have to exist on Magnolia and will be assigned to the user after a successful login. In Keycloak console, select the Mappers tab and click on the create button.

Enter / select the following settings and save your new Mapper:

  • Name: usergroups
  • Mapper Type: Group Membership
  • Token Claim Name: usergroups
  • Add to userinfo: ON

Add a user and group information

Create a group in Keycloak

In Keycloak, under Manage, select Groups and click on New.

Enter superuser for the group name and save it.

Create a test user in Keycloak

In Keycloak under Manage, select Users and click on Add user.

As we want to have a user with superuser permissions in Magnolia AdminCentral, we choose supersso as name. I did not name the user superuser because it should be easy to see later in Magnolia that it’s not the default Magnolia superuser account that was used for log in. I also enabled “Email Verified” even if the mail address is not correct. During the demo I assume this test user is OK. Save the user after entering the details.

Under the Groups tab, in the Available Groups box select superuser and then the Join button.

Now we have prepared a basic Keycloak setup for the integration with the Magnolia SSO Connector module.

Module configuration

It’s assumed you have acquired a license to use the SSO Connector module and have a running Magnolia instance with the module installed.

Retrieve configuration options from Keycloak

We can leverage OpenID Connect Discovery to retrieve most of the needed parameters. In our example paste the following URL in your browser:

http://localhost:8180/auth/realms/Magnolia%20AdminCentral/.well-known/openid-configuration

See well-known-config.json example file.

From above output, copy the following values for:

  • issuer: http://localhost:8180/auth/realms/Magnolia%20AdminCentral
  • authorization_endpoint: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/auth
  • token_endpoint: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/token
  • userinfo_endpoint: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/userinfo
  • end_session_endpoint: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/logout
  • jwks_uri: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/certs

Also have the values for Client ID and Secret available from the Keycloak configuration shown at the beginning of this article.

Login to Magnolia AdminCentral with superuser credentials, open the configuration app and navigate to

/modules/sso-connector/config/authenticationServices

Add a new content node called keycloakOpenIDConnectMagnoliaAdminCentral or rename a template that come with the SSO Connector module when you have samples enabled in your Magnolia instance configuration (otherwise take a sample from the module source).

We add the following properties and values to the keycloakOpenIDConnectMagnoliaAdminCentral content node from the values we collected so far:

  • accessTokenEndpoint: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/token
  • authorizationBaseUrl: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/auth
  • clientId: mgnl-admincentral
  • clientSecret: add your own secret!
  • endSessionEndpoint: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/logout
  • openIdIssuer: http://localhost:8180/auth/realms/Magnolia%20AdminCentral
  • openIdWebKeyUrl: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/certs
  • userInfoURL: http://localhost:8180/auth/realms/Magnolia%20AdminCentral/protocol/openid-connect/userinfo

That’s the basic configuration for the OpenID Connect service. Now add a few more properties needed by the Magnolia module, for this example all of them are mandatory.

  • callbackURL: the context your Magnolia is running and what you have defined in Keycloak (add the /.auth), for my instance: http://localhost:8080/dev/.auth
  • externalGroupsManagement: true
  • openIdAccessTokenAttributeName: openIdToken
  • openIdEnabled: true
  • openIdLogoutEnabled: true
  • scope: openid profile email offline_access
  • storeToken: true

Field mappings

Because many authentication services use different attribute names regarding user information it is necessary to map the retrieved response to a standard set of user attributes used within the SSO Connector module.

On the left side there are the field names provided as “user information” result by your authentication service that are needed to identify a user or are especially interesting for your use case. On the right side you try to match delivered attributes with the SSO Connector standard fields email, family_name, given_name, id and name.

Even if there are attributes defined on the left side that are not matched on the right side, they are still stored within the properties of Magnolia’s info.magnolia.cms.security.ExternalUser object used for external logins. So if you write code to access the currently logged in user, you can access those properties to retrieve additional attributes specified in the field mappings.

Sample authentication service configuration

Example SSO Connector module service configuration file.

Magnolia CMS configuration

Besides the SSO module itself, we also need to configure some basic settings in Magnolia CMS itself.

Filter configuration

Login

In the Magnolia configuration app, navigate to /server/filters/login and replace the value for the class property with

info.magnolia.cms.security.auth.login.SSOAuthenticationLoginFilter

Logout

Optional: if you also want to use the (experimental) logout functionality - navigate to /server/filters/logout and replace the value for the class property with

info.magnolia.cms.security.auth.logout.SSOLogoutFilter

To make use of the logout function, the storeToken property must be configured with the value “true” in your authentication service configuration because usually you have to pass a token value to the server handling the logout (see above).

More changes to the Magnolia configuration

You should also find the sso-authentication node under /server/security/userManagers, the auth mapping under /server/MIMEMapping and the SSOPreserveOriginalURIServlet under /server/filters/servlets. These changes are installed automatically by the SSO Connector module so you don’t have to care about them (unless you want to adjust the settings or classes).

Permissions

Here we define a new superuser group matching the one that we previously created in Keycloak.

Assign the (existing super user and rest-admin roles).

The external group names (or roles if present) must exactly match the group/role names defined in Magnolia to make use of defined permissions!

If everything looks right but SSO login still does not work (eg you encounter an “endless loop”…) then it’s very likely permissions are not correct => the user has authenticated, groups are assigned but permissions in Magnolia are not feasible for access. This can easily happen if you assign the role anonymous to a user that wants to access AdminCentral, this will be blocked because anonymous does not have the right to use the backend. On the other hand, if you want to allow SSO access to a page or section on a public web instance, then the anonymous role must be included in the set of permissions for the authenticated user (so beware of copy and paste between author and public instances).

Magnolia superuser group for bootstrapping.

Client callback

Now we define the path/resource we want to enable SSO login for.

Be careful with what you do here You can easily logout yourself from the Magnolia backend when applying a wrong configuration! So don’t apply this to a production system without proper testing!

If you mess up your login configuration and you are lucky, you can help yourself by calling Magnolia with parameters:

http://localhost:8080/magnoliaAuthor/.magnolia/admincentral?mgnlUserId=superuser&mgnlUserPSWD=superuser

If that does not work, you must rescue yourself.

We create a new client callback named admincentral-sso (you can choose any name for this) and set the properties:

  • authenticationServiceName: keycloakOpenIDConnectMagnoliaAdminCentral (must match exactly the name of the authentication service you defined in the SSO Connector module configuration!)
  • class: info.magnolia.cms.security.auth.callback.SSOAuthenticationRedirectCallback

Under the originalUrlPattern node set

  • patternString: /.magnolia/admincentral (this is the path we protect with SSO)

Order of the client callbacks is important! Place the admincentral-sso before the form node!

Security callback filter configuration for Magnolia.

Using multiple login providers at the same time

Hint: In the above example I set the patternString value to /.magnolia/admincentral and not to /.magnolia.

This means when calling the backend URL (./magnolia) then the original Magnolia login form is presented to the user so you still can login with the local superuser or other local accounts. In addition to that, if you have configured LDAP, then you could still use LDAP, local Magnolia users and SSO users with a different authentication provider all at the same time!

To make it more convenient, I created a virtual URI in the sso-connector module configuration (you can put this into your own module if you want).

With that “trick”, I can trigger an SSO login by just adding the /sso path to my Magnolia author server, eg http://localhost:8080/magnoliaAuthor/sso so I can avoid entering or linking /.magnolia/admincentral.

Virtual URI mapping example.

Testing the setup

LDAP / local user accounts

Call Magnolia AdminCentral

http://localhost:8080/magnoliaAuthor or http://localhost:8080/magnoliaAuthor/.magnolia

The “regular” Magnolia login form is shown:

  • if LDAP is enabled the user first is looked up in LDAP
  • if no LDAP is available or the user does not exist in LDAP, a local Magnolia user account is used

You should be able to login as before the SSO Connector configuration:

SSO / Keycloak accounts

Log out and call

http://localhost:8080/magnoliaAuthor/.magnolia/admincentral or http://localhost:8080/magnoliaAuthor/sso if you have configured the virtual URI.

The Keycloak login screen shows up:

Enter the credentials for the supersso user we created above and log in.

Tada! Now we are logged in with the Keycloak user.

Please also make sure that you indeed have superuser rights on this Magnolia instance (see if all the apps are visible and if you can access configuration. This was achieved by creating and assigning the superuser group in Magnolia.