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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With