Keycloak with Spring Boot KeycloakSecurityContext always null

Viewed 4454

I try to use Keycloak with spring boot but i'm facing problem. Authentication works fine with the adapter but not the authorizations.

This is my configuration :

keycloak.realm = master
keycloak.auth-server-url = http://127.0.0.1:8080/auth
keycloak.ssl-required = none
keycloak.resource = pactng
keycloak.credentials.secret = **************************
keycloak.use-resource-role-mappings = true
keycloak.principal-attribute=preferred_username
keycloak.bearer-only = true

keycloak.policy-enforcer-config.userManagedAccess=org.keycloak.representations.adapters.config.PolicyEnforcerConfig.UmaProtocolConfig
keycloak.policy-enforcer-config.lazyLoadPaths=true

keycloak.policy-enforcer-config.paths[0].path=/*
keycloak.policy-enforcer-config.paths[0].methods[0].method=GET
keycloak.policy-enforcer-config.paths[0].methods[0].scopes[0]=urn:pactng:scopes:read
keycloak.policy-enforcer-config.paths[0].methods[1].method=POST
keycloak.policy-enforcer-config.paths[0].methods[1].scopes[0]=urn:pactng:scopes:create
keycloak.policy-enforcer-config.paths[0].methods[2].method=PUT
keycloak.policy-enforcer-config.paths[0].methods[2].scopes[0]=urn:pactng:scopes:update
keycloak.policy-enforcer-config.paths[0].methods[3].method=PATCH
keycloak.policy-enforcer-config.paths[0].methods[3].scopes[0]=urn:pactng:scopes:update
keycloak.policy-enforcer-config.paths[0].methods[4].method=DELETE
keycloak.policy-enforcer-config.paths[0].methods[4].scopes[0]=urn:pactng:scopes:delete

I'm not defining role here has i want authorization to be dynamically computed by the adapter based on the request and permission contain inside the access token.

And this is my KeycloakConfig for spring security :

@KeycloakConfiguration
public class KeycloakConfig extends KeycloakWebSecurityConfigurerAdapter {

    /**
     * Map keycloak role to spring ROLE_<ROLE>
     * @param auth
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    /**
     * Defined the strategy use by keycloak for user session. We need authorization system so we use a session for a confidential client.
     * Can be NullAuthenticatedSessionStrategy for bearerClient
     * @return
     */
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    /**
     * Use spring boot application.properties instead of keycloak.json to retrieve
     * connexion informations
     */
    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/*").authenticated()
                .anyRequest().permitAll();
    }
}

I test by retrieving token with the following request. UMA 2 is activated as it's not seem's possible to have authorizations enable without it with this adapter.

# Get Access Token for a user.
POST http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token
{
    client_id   "<CLIENT_ID>"
    client_secret   "<CLIENT_SECRET>"
    username    "<USERNAME>"
    password    "<USER_PASSWORD>"
    grant_type  "password"
}

This give me an access token that i use in the following request.

# Try to get access to the resource endpoint
GET http://localhost:8081/api/v1/<resources>
Headers: authorization: Bearer <ACCESS_TOKEN>

This give me a 401 WWW-Authenticate following the UMA2 specification with a ticket. I then contact keycloak to get a RPT :

# Get RPT from Keycloak
POST http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token
Headers: authorization: Bearer <ACCESS_TOKEN>
{
    grant_type: "urn:ietf:params:oauth:grant-type:uma-ticket"
    ticket: <TICKET>
}

Keycloak give me a RPT which is a access token with permissions includes inside it. I then retry to get the ressource but with this RPT

# Try to get access to the resource endpoint
GET http://localhost:8081/api/v1/<resources>
Headers: authorization: Bearer <RPT>

But instead of having access to the resource, the adapter give me again a 401 WWW-Authenticate.

I digged into the code and found that the KeycloakSecurityContext is always null inside AbstractPolicyEnforcer:

   public AuthorizationContext authorize(OIDCHttpFacade httpFacade) {
        EnforcementMode enforcementMode = getEnforcerConfig().getEnforcementMode();
        // Always return null
        KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();

This make the code to be trapped every time inside this portion of code :

if (securityContext == null) {
     if (!isDefaultAccessDeniedUri(request)) {
          if (pathConfig != null) {
               if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
                   return createEmptyAuthorizationContext(true);
               } else {
                   challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
               }
           } else {
                    handleAccessDenied(httpFacade);
           }
      }
      return createEmptyAuthorizationContext(false);
 }

So my question, after this long explanation, is why is this securityContext not populated? Am i missing something?

1 Answers

Ok, I found the solution. It's seems that this adapter want a securityContraints on a role to do it's job. So i add this to make it works:

keycloak.securityConstraints[0].authRoles[0]=*
keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/*

This basically tell it to handle all role on all paths.

P.S Stay with JAVA 11 because it's not compatible with latest JDK version.

Related