Keycloak Spring Boot adapter and anonymous resources

keycloak-spring-boot-2-adapter
keycloak anonymous user
keycloak-spring-security-adapter example
keycloak sso tutorial
keycloak spring boot rest api
keycloak-adapter-bom
keycloak authorization example
keycloak javascript adapter

I cannot get Keycloak to skip authorization checks for a path with public resources.

We are using the Spring Boot Keycloak Adapter.

We have tried the following solutions:

  1. Did not specify path using security constraints in app properties (no mapping for "/static/*")
  2. Use "public" for the following

        keycloak.security-constraints[10].securityCollections[0].name=public
        keycloak.security-constraints[10].securityCollections[0].patterns[0]=/static/*
    
  3. In SecurityConfig.configure()

    http.authorizeRequests().antMatchers("/static/**").permitAll()
    

Read about using a policy enforcer, but there are no instructions on doing this with Spring Boot other than using a policy-enforcer-config. I have no idea where to specify since we don't have a keycloak.json file.

Everything above always results in:

java.lang.ClassCastException: org.springframework.security.authentication.AnonymousAuthenticationToken cannot be cast to org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken
    at org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade.getSecurityContext(SimpleHttpFacade.java:63) ~[keycloak-spring-security-adapter-3.4.2.Final.jar:3.4.2.Final]
    at org.keycloak.adapters.AuthenticatedActionsHandler.corsRequest(AuthenticatedActionsHandler.java:102) ~[keycloak-adapter-core-3.4.2.Final.jar:3.4.2.Final]
    at org.keycloak.adapters.AuthenticatedActionsHandler.handledRequest(AuthenticatedActionsHandler.java:54) ~[keycloak-adapter-core-3.4.2.Final.jar:3.4.2.Final]
    at org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter.doFilter(KeycloakAuthenticatedActionsFilter.java:78) ~[keycloak-spring-security-adapter-3.4.2.Final.jar:3.4.2.Final]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at com.testing.SimpleCORSFilter.doFilter(SimpleCORSFilter.java:51) ~[classes/:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter.doFilter(KeycloakSecurityContextRequestFilter.java:79) ~[keycloak-spring-security-adapter-3.4.2.Final.jar:3.4.2.Final]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter.doFilter(KeycloakAuthenticatedActionsFilter.java:82) ~[keycloak-spring-security-adapter-3.4.2.Final.jar:3.4.2.Final]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:84) ~[keycloak-spring-security-adapter-3.4.2.Final.jar:3.4.2.Final]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) ~[tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:94) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve.invoke(AbstractAuthenticatedActionsValve.java:67) [spring-boot-container-bundle-3.4.2.Final.jar:3.4.2.Final]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:616) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve.invoke(AbstractKeycloakAuthenticatorValve.java:181) [spring-boot-container-bundle-3.4.2.Final.jar:3.4.2.Final]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:502) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1132) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1533) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1489) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_152]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_152]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.0.43.jar:8.0.43]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_152]

Any help will be appreciated.

Thank you.

Other posts mentioned calling web.ignoring()..., but I was overriding the configure(HttpSecurity) method which doesn't have the ignoring method.

I finally realized that I can override both configure methods (HttpSecurity and WebSecurity) to accomplish this.

public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/static/*");
}

It would be nice if we could specify anonymous access using keycloak parameters in the application properties file, but this works.

I cannot get Keycloak to skip authorization checks for a path with public resources. We are using the Spring Boot Keycloak Adapter. We have  Anonymous Access For Public Endpoints/Resources in Spring Boot Adapter. Expected that any public resources defined in the keycloak.securityConstraints [0]

The issue can be resolved by adding below code as its provided into https://issues.jboss.org/browse/KEYCLOAK-6468

@Override
public void configure(final WebSecurity web) throws Exception {
   web.ignoring().antMatchers("/keycloak.json"); 
}

I cannot get Keycloak to skip authorization checks for a path with public resources. We are using the Spring Boot Keycloak Adapter. We have tried the following  Ensure your WAR application imports org.keycloak.adapters.jetty and maybe some more packages in the META-INF/MANIFEST.MF file, under the Import-Package header. Using maven-bundle-plugin in your project properly generates OSGI headers in manifest.

I had a similar desired configuration in my application and was running into the same exception. I was able to get around it by overriding a few of Keycloak's classes and adding an isAssignableFrom(Class) check in HTTP facade's getSecurityContext() method:

/**
 * Hack to fix issue accessing resources anonymously while Keycloak adapter is part of the security chain.
 */
public class FixedKeycloakAuthenticatedActionsFilter extends KeycloakAuthenticatedActionsFilter {

    private static final Logger log = LoggerFactory.getLogger(FixedKeycloakAuthenticatedActionsFilter.class);

    private ApplicationContext applicationContext;
    private AdapterDeploymentContext deploymentContext;

    FixedKeycloakAuthenticatedActionsFilter() {
        super();
    }

    /*
     * Must override this because deploymentContext is set to private on the super class
     */
    @Override
    protected void initFilterBean() throws ServletException {
        super.initFilterBean();

        deploymentContext = applicationContext.getBean(AdapterDeploymentContext.class);
    }

    /*
     * Must override this because applicationContext is set to private on the super class
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        super.setApplicationContext(applicationContext);

        this.applicationContext = applicationContext;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        // use our fixed facade class
        HttpFacade facade = new FixedSimpleHttpFacade((HttpServletRequest)request, (HttpServletResponse)response);
        AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deploymentContext.resolveDeployment(facade), (OIDCHttpFacade)facade);
        boolean handled = handler.handledRequest();
        if (handled) {
            log.debug("Authenticated filter handled request: {}", ((HttpServletRequest) request).getRequestURI());
        } else {
            chain.doFilter(request, response);
        }
    }

    /**
     * Hack to add missing class check on {@link SimpleHttpFacade#getSecurityContext()}.
     */
    static class FixedSimpleHttpFacade extends SimpleHttpFacade {
        /**
         * Creates a new simple HTTP facade for the given request and response.
         *
         * @param request  the current <code>HttpServletRequest</code> (required)
         * @param response the current <code>HttpServletResponse</code> (required)
         */
        FixedSimpleHttpFacade(HttpServletRequest request, HttpServletResponse response) {
            super(request, response);
        }

        @Override
        public KeycloakSecurityContext getSecurityContext() {
            SecurityContext context = SecurityContextHolder.getContext();

            if (context != null && context.getAuthentication() != null &&
                    // this check is missing in the super class
                    KeycloakAuthenticationToken.class.isAssignableFrom(context.getAuthentication().getClass())
                ) {
                KeycloakAuthenticationToken authentication = (KeycloakAuthenticationToken) context.getAuthentication();
                return authentication.getAccount().getKeycloakSecurityContext();
            }

            return null;
        }
    }
}

Then I override the method that returns that particular filter in my KeycloakWebSecurityConfigurerAdapter subclass:

/**
 * Override to return our fixed filter.
 * @return An instance of our fixed {@link KeycloakAuthenticatedActionsFilter}.
 */
@Bean
@Override
protected KeycloakAuthenticatedActionsFilter keycloakAuthenticatedActionsFilter() {
    return new FixedKeycloakAuthenticatedActionsFilter();
}

Now I am able to hit my public endpoints with no auth and my secured endpoints still work as expected.

This seems like a simple check...I am not sure why it is not included in the SimpleHttpFacade that ships with the Keycloak Spring Security adapter.

Secure a Spring Boot application using Keycloak for user authentication a Spring Boot application for a public library, define the application resources, Keycloak client adapters are libraries that make it very easy to secure  Keycloak Spring Boot adapter and anonymous resources. 3. How to Setup Keycloak + Spring Boot + Oauth2 within Microservice Architecture. Hot Network Questions

If you're using keycloak-spring-boot adapter, you just need to avoid declare a Role for that security constraint, for example:

    keycloak:
      realm: Demo
      auth-server-url: http://localhost:8080/auth
      ssl-required: none
      resource: demo-service
      credentials:
        secret: your-service-credentials
      bearer-only: true
      principal-attribute: preferred_username
      use-resource-role-mappings: true
      security-constraints:
        -
          authRoles:

          securityCollections:
            -
              name: public
              patterns:
                - /actuator/health
        -
          authRoles:
            - SECURE_ROLE
          securityCollections:
            -
              name: protected
              patterns:
                - /secure/*

In YAML file above, I declare the URL /actuator/health as public, as you can see, it doesn't have a Role. The user needs a SECURE_ROLE to get access to all links under /secure/ URL.

Learn how to configure a Keycloak server and use it with a Spring The access token should be used in every request to a Keycloak-protected resource by The Keycloak Spring Boot adapter capitalizes on Spring Boot's  Set up a Spring Boot application for Keycloak; Configure the Keycloak integration with Spring Boot; Define the application resources; Add access policies based on user roles. You can check out the full source code of the demo project we're going to build on GitHub. Let's get started! 2. The Demo Application

Securing a REST API with Spring Boot and Keycloak access for secure endpoints and allowing anonymous access for public ones Single-Sign On; Identity Brokering and Social Login; User Federation; Client Adapters; Admin /​spring-keycloak-example/blob/master/src/main/resources/application.yml In previous releases, Spring Boot applications had to manually implement the KeycloakConfigResolver interface or extend the built-in org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver. This release fixes the backward compatibility issue by resolving instances automatically in case none is provided.

Component/s: Adapter - Java - Spring Security and springboot properties config content is ``` keycloak: realm: gov-approve resource: gov-approve-app Keycloak is an open source Identity and Access Management solution targeted towards modern applications and services. Keycloak offers features such as Single-Sign-On (SSO), Identity Brokering and Social Login, User Federation, Client Adapters, an Admin Console, and an Account Management Console. To learn more about Keycloak, please visit the official page.

Protecting Resources; 2.3.5. The first thing you must do is create a keycloak.​json adapter configuration file within the WEB-INF directory The Keycloak Spring Boot adapter takes advantage of Spring Boot's The anonymous requests (requests without any token) are allowed just for creating (registration) of new clients. Home » org.keycloak » keycloak-spring-boot-adapter Keycloak Spring Boot Integration. Keycloak Spring Boot Integration License: Apache 2.0: Tags: spring: Used By: 8

Comments
  • How have you resolved this issue?
  • @Teo It's not working properly for me either. They have this ticket closed on their JIRA but I'm still getting java.lang.ClassCastException: org.springframework.security.authentication.AnonymousAuthenticationToken cannot be cast to org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken
  • @doublemc and so? what can we do?
  • @Teo haven't found anything that works properly - thought you might have on your own ;)
  • @doublemc it seem like we are not alone :P stackoverflow.com/questions/50228797/…