I\'m using Spring Boot
and Keycloak
to develop a web-app.
Then I wrote a scheduled task where I\'m using the KeycloakRestTemplate
to ask s
If you want to send a request like below through spring
POST /auth/realms/demo/protocol/openid-connect/token
Authorization: Basic cHJvZHVjdC1zYS1jbGllbnQ6cGFzc3dvcmQ=
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
what you need is something like
RestTemplate template = new RestTemplate();
String uri = "https://host:port/auth/realms/demo/protocol/openid-connect/token";
String data = "grant_type=client_credentials" ;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add(HttpHeaders.AUTHORIZATION, "Basic " + Base64Utils.encodeToString("clientId:clientSecret".getBytes()));
HttpEntity<String> requestEntity = new HttpEntity<String>(data, headers);
ResponseEntity<JsonNode> result = template.exchange(uri, HttpMethod.POST, requestEntity, JsonNode.class);
JsonNode jn = result.getBody();
String access_token = jn.get("access_token").asText();
Replace clientId
and clientSecret
with actual values.
Update:-
Referring to the link that you have mentioned in your question, you can generate the jwt bearer token (client access token) by keycloak itself. Once you get the jwt token, your subsequent requests to resource server should contain header
Authorization: Bearer <jwt bearer token>
In the post by @Xtreme Biker on topic Keycloak spring security client credential grant , he has provided some sample code for perhaps how you can achieve this using interceptor approach.
MORE UPDATE :-
According to keycloak docs - https://www.keycloak.org/docs/3.1/securing_apps/topics/oidc/java/java-adapter-config.html
to set the jks you should have following properties in your application.properties file.
keycloak.client-keystore-password=PWD
keycloak.client-keystore=classpath:CLIENT.jks
keycloak.client-key-password=PWD
and according to - https://www.keycloak.org/docs/3.1/securing_apps/topics/oidc/java/spring-security-adapter.html , once you set the jks properly,KeycloakRestTemplate
should be able to add proper authentication header to your request.
UPDATE 3 :-
After going through - https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/java/spring-security-adapter.adoc , I firmly believe that KeycloakRestTemplate
should be able to add the required jwt token to the authorization header of the request because it uses KeycloakClientRequestFactory
which gets token string from KeycloakSecurityContext
.
Please try to add all the configuration suggested in this doc like all the filter bean configurations
@Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(
KeycloakAuthenticatedActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakSecurityContextRequestFilterBean(
KeycloakSecurityContextRequestFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
and ensure you provide all the required properties in the application.properties file.
Since you are shooting the request from scheduled task which may be asyc in nature so you may have to change the strategy in the constructor of your SecurityConfig
, so that instead of a ThreadLocal
SecurityContext
an InheritableThreadLocal
is used, which passes this information when spawning childthreads.
public SecurityConfig ( KeycloakClientRequestFactory keycloakClientRequestFactory ) {
this.keycloakClientRequestFactory = keycloakClientRequestFactory ;
// to use principal and authentication together with @ async
SecurityContextHolder.setStrategyName ( SecurityContextHolder.MODE_INHERITABLETHREADLOCAL ) ;
}
Ref - https://translate.google.co.in/translate?hl=en&sl=de&u=https://blog.codecentric.de/2017/09/keycloak-und-spring-security-teil-3-kommunikation-via-keycloakresttemplate/&prev=search for more details.
Hope this will help you some way in resolving your issue.
By default SecurityContextHolder use a THREAD_LOCAL strategy, and thus other threads don't inherit the context from their parent. Look at the Javadoc for MODE_INHERITABLETHREADLOCAL to see if it corresponds to your needs.
Your method being an @Scheduled it is executed in a different thread than the caller thread. SecurityContextHolder being a Thread Local will not propagate the security context to the scheduled thread. One method would be to change the default strategy of the SecurityContextHolder from THREAD_LOCAL, to MODE_INHERITABLETHREADLOCAL, by adding this line in the default constructor of a @Configuration class.
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
But depending on your use case, you should be aware that the security context might contain sensitive information that you might not want to be passed down.