Spring Security/OAuth 2.0 SSO Integration
Overview
This page explains how to integrate Bloomreach Content with Spring Security and an OAuth 2.0 identity provider for single sign-on (SSO).
When to Use
Use this approach to enable SSO for the Bloomreach Content CMS using Spring Security and any OAuth 2.0-compliant identity provider.
Prerequisites
- brXM 14.6 or later (Spring Boot enabled by default)
- Familiarity with Spring Security and OAuth 2.0 concepts
- Access to an OAuth 2.0 identity provider (e.g., Azure Entra ID, Keycloak)
Implementation
Spring Boot and Spring Security Integration
Bloomreach Content CMS ships with Spring Boot starting from version 14.6. This enables you to use Spring Security for SSO configuration. To simplify dependency management, add the Spring Bill of Materials (BOM) to your project.
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>${spring.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-bom</artifactId> <version>${spring-security.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency>
Some projects may use a Spring ContextLoaderListener in web.xml and have Spring Boot disabled. For details, see Upgrade 14.5 to 14.6.
Warning: The legacy Spring Security Extension (used for SAML SSO) is no longer maintained and is incompatible with Java 11. Use Spring Security for new integrations.
Add your project-specific Spring configuration in the CMS. Place any Java-based Spring Boot configuration in the org.bloomreach.xm.cms package. Include your Spring Security configuration in this package.
@Configuration @EnableWebSecurity public class SecurityConfiguration { @Bean SecurityFilterChain configure(HttpSecurity http) throws Exception { // Your SSO configuration return http.build(); } }
Spring Boot automatically detects Java-based configuration in org.bloomreach.xm.cms due to framework conventions.
CMS (Repository) Login Integration
Spring Security authentication does not automatically propagate to the CMS repository. The CMS must be informed about the authenticated user to apply its internal security model for authorization. Implement a filter in your security configuration to pass the authenticated user's information (username) to the CMS. This ensures the user's session is authorized in the repository.
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (!authentication.isAuthenticated()) { log.error("User not authenticated"); chain.doFilter(request, response); return; } // Check if the user already has a SSO user state stored in HttpSession before. final Object principal = authentication.getPrincipal(); if (principal instanceof AuthenticatedPrincipal) { final String username = ((AuthenticatedPrincipal) principal).getName(); if (StringUtils.isNotBlank(username)) { SimpleCredentials credentials = new SimpleCredentials(username, "DUMMY".toCharArray()); credentials.setAttribute("type", "sso"); req.setAttribute(UserCredentials.class.getName(), new UserCredentials(credentials)); } }
CMS Authentication
By default, the CMS uses a repository-based user manager for authentication. To delegate authentication to Spring Security, configure an additional security provider that overrides the authentication method.
Add the following configuration to register a custom security provider:
definitions: config: /hippo:configuration/hippo:security/sso: jcr:primaryType: hipposys:securityprovider hipposys:classname: org.bloomreach.xm.cms.sso.SSODelegatingSecurityProvider /hipposys:userprovider: jcr:primaryType: hipposys:userprovider hipposys:dirlevels: 0 /hipposys:groupprovider: jcr:primaryType: hipposys:groupprovider hipposys:dirlevels: 0
The custom SSODelegatingSecurityProvider overrides authentication to use Spring Security, while delegating other operations to the configured user and group managers.
/** * Custom <code>org.hippoecm.repository.security.SecurityProvider</code> implementation. * Hippo Repository allows setting a custom security provider for specific users (e.g., SSO). * If a user is associated with a custom security provider, Hippo Repository invokes * the custom provider for authentication and authorization. */ public class SSODelegatingSecurityProvider extends DelegatingSecurityProvider { private static Logger log = LoggerFactory.getLogger(SSODelegatingSecurityProvider.class); private HippoUserManager userManager; /** * Constructs by creating the default <code>RepositorySecurityProvider</code> to delegate all calls * except authentication. * * @throws RepositoryException */ public SSODelegatingSecurityProvider() throws RepositoryException { super(new RepositorySecurityProvider()); } /** * Returns a custom (delegating) HippoUserManager to authenticate a user by Spring Security. */ @Override public UserManager getUserManager() throws RepositoryException { if (userManager == null) { userManager = new DelegatingHippoUserManager((HippoUserManager) super.getUserManager()) { @Override public boolean authenticate(SimpleCredentials creds) { return validateAuthentication(creds); } }; } return userManager; } /** * Returns a custom (delegating) HippoUserManager to authenticate a user by Spring Security. */ @Override public UserManager getUserManager(Session session) throws RepositoryException { return new DelegatingHippoUserManager((HippoUserManager) super.getUserManager(session)) { @Override public boolean authenticate(SimpleCredentials creds) { return validateAuthentication(creds); } }; } /** * Validates authentication in Spring Security. * * @param creds * @return * @throws RepositoryException */ protected boolean validateAuthentication(SimpleCredentials creds) { log.debug("Spring security context validates authentication for credentials: {}", creds); final SecurityContext context = SecurityContextHolder.getContext(); if(context != null) { final Authentication authentication = context.getAuthentication(); if(authentication != null) { log.debug("User {} authenticated: {}", creds.getUserID(), authentication.isAuthenticated()); return authentication.isAuthenticated(); } } return false; } }
User Logout
When a user logs out from the CMS, the internal logout process runs as usual. You must also trigger the logout process in Spring Security. Implement a CMS logout service that redirects the request to the desired endpoint:
/** * Logout service to redirect user after internal logout is done. */ public class LogoutService extends CmsLogoutService { public LogoutService(IPluginContext context, IPluginConfig config) { super(context, config); } @Override protected void redirectPage() { throw new RedirectToUrlException("/logout"); } }
Spring Security processes the /logout endpoint automatically. By default, accessing /logout invalidates the HTTP session, clears any configured rememberMe() authentication, clears the SecurityContextHolder, and redirects to /login?success.
If you need to customize the logout process, implement a LogoutSuccessHandler in your security configuration. The exact approach depends on your identity provider.
Verification
- After configuration, log in to the CMS using your OAuth 2.0 identity provider.
- Confirm that the user is authenticated in both Spring Security and the CMS repository.
- Log out from the CMS and verify that the Spring Security session is also terminated.
Example: Azure Entra ID
A reference implementation based on a v16 Bloomreach Content archetype is available at https://github.com/bloomreach/brxm-azure-sso-oauth2. This project uses LDAP for authentication and the Spring Boot starter for Azure to integrate with Entra ID.
For details on integrating a generic Spring web application with Entra ID, see the official guide. The project README provides an overview of the implementation classes.
A Docker-based local SSO setup using Keycloak and an LDAP server is also included for testing.