1

I'm trying to implement the Service Accounts using Keycloak and Spring boot, to secure an async scheduled task.

I think I need it beacuse otherwise I don't have the credential to access to my service.

Here the guide of keycloak: https://www.keycloak.org/docs/latest/server_admin/index.html#_service_accounts

I tried something like:

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
    map.add("grant_type", "client_credentials");

    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);


    ResponseEntity<String> response = keycloakRestTemplate.postForEntity(URI.create("http://MYURL/auth/realms/MYREALM/protocol/openid-connect/token"), request , String.class );

But I obtain this error:

org.springframework.web.client.HttpClientErrorException: 400 Bad Request
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:85) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:708) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:661) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:636) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
    at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:431) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]

This is my security config:

    Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {  

    @Value("${app.mux}")
    private String mux; 

    @Autowired
    public KeycloakClientRequestFactory keycloakClientRequestFactory;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
            ..................
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public KeycloakRestTemplate keycloakRestTemplate() {
        return new KeycloakRestTemplate(keycloakClientRequestFactory);
    }

    @Bean
    public KeycloakConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    @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;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web
           .ignoring()
           .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/webjars/**");
    }

So how can I implement a request like above?
Droide
  • 1,807
  • 2
  • 17
  • 30

1 Answers1

0

You also need the client_id and client_secret to make the authentication as a service (I guess your client type is confidential). From the Oauth2 spec:

2.3.1. Client Password

Clients in possession of a client password MAY use the HTTP Basic
authentication scheme as defined in [RFC2617] to authenticate with
the authorization server. The client identifier is encoded using the "application/x-www-form-urlencoded" encoding algorithm per Appendix B, and the encoded value is used as the username; the client password is encoded using the same algorithm and used as the
password. The authorization server MUST support the HTTP Basic
authentication scheme for authenticating clients that were issued a
client password.

For example (with extra line breaks for display purposes only):

Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3

Alternatively, the authorization server MAY support including the
client credentials in the request-body using the following
parameters:

client_id REQUIRED. The client identifier issued to the client during the registration process described by Section 2.2.

client_secret REQUIRED. The client secret. The client MAY omit the parameter if the client secret is an empty string.

So add them to your code:

map.add("client_id", "my_application");
map.add("client_secret", "" /*Provide the value given by keycloak*/);

You can retrieve the autogenerated client secret, or even change it from the admin panel:

enter image description here

Community
  • 1
  • 1
Aritz
  • 30,971
  • 16
  • 136
  • 217
  • thank you! but I'm looking for a solution using the JWT... Actually I also read the following question, where you already commented it: https://stackoverflow.com/questions/49900124/can-i-pass-a-principal-with-rest-template-if-im-using-an-async-task-wiht-spring/50085720?noredirect=1#comment87193227_50085720 but I don't figure out very well how implement the code – Droide Apr 29 '18 at 16:40
  • Please, add your full requirements in the question.. Otherwise you make people work for nothing. – Aritz Apr 29 '18 at 16:56