It does not have HTTP ok status - Spring boot keycloak version 4.0.0 final behind haproxy - spring

I'm having an issue with spring boot application authenticated using keycloak, I have this application sitting behind a haproxy and have tried to completely disable cors on the spring app and manage this on the proxy side, however Im still having issues with cors.
"It does not have HTTP ok status"
Note I'm using an older version of the spring boot keycloak plugin due to the original application using spring boot version 1.5.10
Please find attached some of the configuration options I have explored:
Case no 1: Disable cors on spring app -------------------------------------------------
This setup returns http status ok not present on response preflight header
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
public SecurityWithoutCsrfConfig() {
super();
}
// Submits the KeycloakAuthenticationProvider to the AuthenticationManager
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
// Specifies the session authentication strategy
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**",
"/v2/api-docs",
"/swagger-ui.html",
"/swagger2-ui.html",
"/springfox/**",
"/v2/swagger.json",
"/_twilio/**",
"/webjars/**",
"/configuration/**",
"/swagger-resources/**");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/mypath/**").hasAnyRole("USER")
.antMatchers("/mypath_admin/**").hasAnyRole("USER_ADMIN", "ADMIN")
.anyRequest().fullyAuthenticated()
.and().httpBasic().and().cors().disable();
http.headers().cacheControl();
}
Case no 2 : set the headers on spring app side -----------------------------------------------
SecurityConfig
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
public SecurityWithoutCsrfConfig() {
super();
}
// Submits the KeycloakAuthenticationProvider to the AuthenticationManager
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
// Specifies the session authentication strategy
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**",
"/v2/api-docs",
"/swagger-ui.html",
"/swagger2-ui.html",
"/springfox/**",
"/v2/swagger.json",
"/_twilio/**",
"/webjars/**",
"/configuration/**",
"/swagger-resources/**");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/mypath/**").hasAnyRole("USER")
.antMatchers("/mypath_admin/**").hasAnyRole("USER_ADMIN", "ADMIN")
.anyRequest().fullyAuthenticated()
.and().httpBasic();
http.headers().cacheControl();
}
WebConfig
public class WebConfig extends WebMvcConfigurerAdapter {
/**
* Adds Cross Origin Resource Sharing filter
* #return CorsFilter
*/
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
For case 2 i encounter problems with the allowed origin headers. My haproxy config is standard setup you would find on any of the official haproxy site.
Any solutions or suggestions to the above is much appreciated...
Preferably a solution to the http status on the preflight response.

To debug the situation fuuther I added a logger to the configure method in my securityconfig class, as this is where I suspected the requests were breaking down
LOG.info(" --executing: configure(HttpSecurity)");
The factI had a logger in this class now meant that more config details would be printed to the output of the application, so I built the jar, copied it to the server and ran my jar file. When I reloaded the request that originally gave me the cors error (http okay status not present on pre-flight) the logs printed out an SSL handshake exception, this told me that somewhere in my app configuration it was set to only expect connections via https or a secure port. However this was not the case, I was using haproxy exposed on a secure port, forwarding the connections to the port the jar was listening on (8080).
So I double checked all of the configs for security throughout the app, and in my application properties I discovered I had the wrong setting for
keycloak.ssl-required=all
So I removed this setting, deployed the app and voila, issue averted. Hope this comes in as a help to anyone who has similar issues.

Related

Spring Boot 3 security cannot access H2 console - 403

I'm in the process of re-learning Spring security in Spring Boot 3. Things changed a little and for some reason the same settings working for WebSecurityConfigurerAdapter's config method will not work in SecurityFilterChain.
HERE IS SOME CODE FROM PREVIOUS SETUPS- WORKING
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AppUserService userService;
private final PasswordEncoder bCryptPasswordEncoder;
public SecurityConfig(AppUserService userService, PasswordEncoder bCryptPasswordEncoder) {
this.userService = userService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
private static final String[] SWAGGER = {
"/v2/api-docs",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**",
"/v3/api-docs/**",
"/swagger-ui/**"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.cors(c -> {
CorsConfigurationSource cs = request -> {
CorsConfiguration cc = new CorsConfiguration();
cc.setAllowedOrigins(List.of("*"));
cc.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
cc.setAllowedHeaders(List.of("Origin", "Content-Type", "X-Auth-Token", "Access-Control-Expose-Header",
"Authorization"));
cc.addExposedHeader("Authorization");
cc.addExposedHeader("User-Name");
return cc;
};
c.configurationSource(cs);
});
http.headers().frameOptions().disable();
http.sessionManagement().sessionCreationPolicy(STATELESS);
http.authorizeRequests().antMatchers("/login/**").permitAll();
http.authorizeRequests().antMatchers("/h2-console/**").permitAll();
http.authorizeRequests().antMatchers(SWAGGER).permitAll();
http.authorizeRequests().antMatchers("/users/password-reset-request").permitAll();
http.authorizeRequests().antMatchers("/users/password-change").permitAll();
http.authorizeRequests().antMatchers("/users/**").hasAnyAuthority("ADMIN");
http.authorizeRequests().antMatchers("/favorites/**").hasAnyAuthority("USER");
http.authorizeRequests().antMatchers(GET).hasAnyAuthority("USER");
http.authorizeRequests().antMatchers(POST).hasAnyAuthority("USER");
http.authorizeRequests().antMatchers(PUT).hasAnyAuthority("USER");
http.authorizeRequests().antMatchers(DELETE).hasAnyAuthority("MODERATOR");
http.authorizeRequests().anyRequest().authenticated();
http.addFilter(new CustomAuthenticationFilter(authenticationManager()));
http.addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected UserDetailsService userDetailsService() {
return userService;
}
}
NOW SINCE
WebSecurityConfigurerAdapter
is no longer available:
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final AuthenticationProvider authenticationProvider;
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.headers().frameOptions().disable();
http.sessionManagement().sessionCreationPolicy(STATELESS);
http.authorizeHttpRequests().requestMatchers("/h2-console/**").permitAll();
http.authorizeHttpRequests().requestMatchers("/auth/**").permitAll();
http.authorizeHttpRequests().anyRequest().authenticated();
http.authenticationProvider(authenticationProvider);
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Long story short- previously working
http.headers().frameOptions().disable();
http.authorizeRequests().antMatchers("/h2-console/**").permitAll();
Will not work as
http.headers().frameOptions().disable();
http.authorizeHttpRequests().requestMatchers("/h2-console/**").permitAll();
Application.properties for H2 database are exact same, copied. I didn't changed default url for H2. It seems its the Spring Security standing in the way.
Please advice what to do.
Do you have any knowlage if anything changed for H2 setup since previous Spring Boot?
EDIT: If I simply http.authorizeHttpRequests().anyRequest().permitAll();, the console will work. It must be security related
The H2ConsoleAutoConfiguration will register a Servlet for H2's Web Console, therefore, the servletPath property is needed in order to use the MvcRequestMatcher, like so:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) {
// ...
MvcRequestMatcher h2RequestMatcher = new MvcRequestMatcher(introspector, "/**");
h2RequestMatcher.setServletPath("/h2-console");
http.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(h2RequestMatcher).permitAll()
// ...
);
}
In summary, we are permitting every (/**) request under the h2-console servlet path.
Another option is to use PathRequest.toH2Console() as shown in the Spring Boot H2 Console's documentation, which in turn will create an AntPathRequestMatcher for you.
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) {
// ...
http.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(PathRequest.toH2Console()).permitAll()
// ...
);
}
This problem has also been answered in the project's repository

Keycloak return 401 instead of 302 when token expire keyclaok

When the token expires Keycloak normally return 302(redirect to logout) ; however , I want to return 401 instead of 302 in the api response .
I am using spring boot framework , here the keyloack configuration
#KeycloakConfiguration
#EnableGlobalMethodSecurity(jsr250Enabled = true)
public class GlobalSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public KeycloakClientRequestFactory keycloakClientRequestFactory;
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KeycloakRestTemplate keycloakRestTemplate() {
return new KeycloakRestTemplate(keycloakClientRequestFactory);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.and()
.cors()
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("test/login").permitAll()
.anyRequest()
.authenticated();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
// Starting from Keycloak Spring Boot Adapter 7.0.0,
// due to some issues, the automatic discovery of the Keycloak configuration
// from the application.properties (or application.yml) file will not work.
// To overcome this problem, we need to define a KeycloakSpringBootConfigResolver bean explicitly in a #Configuration class.
#Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
but I can not reach in which place It sends 302 when token expire to change the response to 401
According to the documentation, you could use the exceptionHandling() method:
https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/web/server/ServerHttpSecurity.html#exceptionHandling(org.springframework.security.config.Customizer)
exceptionHandling
public ServerHttpSecurity
exceptionHandling​(Customizer<ServerHttpSecurity.ExceptionHandlingSpec>
exceptionHandlingCustomizer)
Configures exception handling (i.e. handles when authentication is
requested). An example configuration can be found below:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.exceptionHandling((exceptionHandling) ->
exceptionHandling
// customize how to request for authentication
.authenticationEntryPoint(entryPoint)
);
return http.build();
}
Parameters:
exceptionHandlingCustomizer - the Customizer to provide more options for the ServerHttpSecurity.ExceptionHandlingSpec
Returns:
the ServerHttpSecurity to customize
and according to this page, https://github.com/spring-projects/spring-boot/issues/10715
You could simply do:
http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
However I think this will always return 401 UNAUTHORIZED on any (Authentication Exceptions) and not specifically Token Expired.
If you look at the documentation further, you can implement a custom ServerAuthenticationEntryPoint
https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/server/ServerAuthenticationEntryPoint.html
Example:
#Component
#Slf4j
public class GatewayAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
#Override
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
// resolve response status
if (ex instanceof AccessDeniedException) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
} else exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// compose error response
final ErrorResponse error = new ErrorResponse(ex, exchange.getRequest().getURI().getPath(),
exchange.getRequest().getMethod(), HttpStatus.UNAUTHORIZED);
exchange.getResponse()
.writeWith(getEncoder().encode(Mono.just(error), exchange.getResponse().bufferFactory(),
ResolvableType.forInstance(error), MediaType.APPLICATION_JSON,
Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix())));
return Mono.error(ex);
}
}

Spring erro Cors

I have a problem with the spring Cors.
I get this error on chome:
Access to XMLHttpRequest at 'http://localhost:8080/api/informationWS' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
My file WebSecurityConfigurerAdapter
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LoginService loginService;
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(loginService)
.passwordEncoder(this.passwordEncoderAutentication());
}
#Bean
public PasswordEncoder passwordEncoderAutentication() {
String idForEncode = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);
return passwordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.cors();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
My file ResourceServerConfigurerAdapter
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/informationWS").permitAll()
.antMatchers(HttpMethod.POST, "/api/work").authenticated()
.anyRequest().denyAll();
}
}
I tried to work with Cors in the two ways below, but neither of them worked, generating the same error
My file cors
#Configuration
#EnableWebMvc
public class Cors implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:4200");
}
}
My file Cors2
#Configuration
public class Cors {
#Bean
public FilterRegistrationBean<CorsFilter> corsFilterFilterRegistrationBean(){
List<String> host = Arrays.asList("http://localhost:4200");
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(host);
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", corsConfiguration);
CorsFilter corsFilter = new CorsFilter(source);
FilterRegistrationBean<CorsFilter> filter = new FilterRegistrationBean<>(corsFilter);
filter.setOrder(Ordered.HIGHEST_PRECEDENCE);
return filter;
}
}
What you could try/check:
check if the application code is executed - maybe server stops execution for some reason, and so your spring code cannot add a header.
maybe there is preflight request and server does not allow it (so again server stopped execution and your backend code could not send the header)
maybe you yourself stop script somewhere before the header is added, like System.exit(0);
maybe there is redirect to code which does not add header, for example some exception
try running the request from Postman - you should not get the error and maybe you will see something surprising.
does this .antMatchers(HttpMethod.GET, "/api/informationWS") really match the request? Maybe there is a way to add wildcard just for testing and see if it works? Are you sending GET request?
More details, technologies different but concept same: https://dariuscoder.com/2021/09/16/how-to-debug-cors/

CORS problems with Spring Security and Websocket

I am developing an Ionic app based on a Spring backend.
I implemented Spring Security with JWT authentication. My app will have a chat room where users can talk each other in private or public chat. So, I am implementing a WebSocket system in order to get all updates in real time.
This is my Security Configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private UserDetailsService userDetailsService;
private AuthenticationManager authenticationManager;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(this.userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter();
}
// configurazione Cors per poter consumare le api restful con richieste ajax
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");
configuration.setAllowedMethods(Arrays.asList("POST, PUT, GET, OPTIONS, DELETE"));
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().cors().and()
.authorizeRequests()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/image/**").permitAll()
.antMatchers("/socket/**").permitAll()
.antMatchers("/public/**").permitAll().and()
.authorizeRequests().anyRequest().authenticated().and();
httpSecurity.headers().cacheControl();
}
#Bean
public AuthenticationManager customAuthenticationManager() throws Exception {
return authenticationManager();
}
}
This is my WebSocket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/socket")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/chat")
.enableSimpleBroker("/subscribe");
}
}
In this condition, I am currently facing this error:
Access to XMLHttpRequest at
'http://localhost:8080/SpringApp/socket/info?t=1547732425329' from
origin 'http://localhost:8100' has been blocked by CORS policy: The
value of the 'Access-Control-Allow-Origin' header in the response must
not be the wildcard '*' when the request's credentials mode is
'include'. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
Each call is working (i am perfectly authorized with jwt) but the WebSocket can't work.
So, I tried to simply remove the .cors() in configure method in my security configuration class. This lead me to an opposite problem:
error in chrome
Indeed, now WebSocket works perfectly, instead each api call gives me 401.
What's the correct way to resolve this problem?
Thank you
Yeah, I got the same error when I was working in a related issue in one of my projects. The solution was that I had to set the allowed-origin header value to the URL of my application. The wildcard value (*) is not allowed if you send credentials.

SSO with keyclock Spring boot and JWT

Background:
Recently I was playing around with Oauth2 and Single Sign-on features. I have successfully implemented a POC with Spring-Boot, Keycloak. I can be found here: https://github.com/rivu007/sso-springboot-keyclock
This a REST service and I am using keycloak as OAuth2 server to secured protected endpoints. The setup works great with access token (clientId and secret)
Complication
I would love to extend the POC with JWT. Keyclock provides signed JWT as client authenticator. I have generated the keys and certificate (keystore.jks) and updated the application.yml file accordingly:
keycloak:
auth-server-url: http://localhost:18080/auth
realm: sso
resource: product-app
credentials:
jwt:
client-keystore-file: "src/main/resources/keystore.jks"
client-keystore-type: "jks"
client-keystore-password: "storepw"
client-key-password: "keypw"
client-key-alias: "product-app"
token-expiration: 10
confidential-port: 0
use-resource-role-mappings: true
ssl-required: none
And SecurityConfiguration.java looks like this:
#Configuration
#EnableWebSecurity
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
#EnableGlobalMethodSecurity(prePostEnabled = true)
#KeycloakConfiguration
public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public KeycloakClientRequestFactory keycloakClientRequestFactory;
#Autowired
private UserDetailsService userDetailsService;
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KeycloakRestTemplate keycloakRestTemplate() {
return new KeycloakRestTemplate(keycloakClientRequestFactory);
}
/**
* Defines the session authentication strategy.
*/
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http.csrf().disable()
.authorizeRequests()
// Allow anonymous access to "/" path
.antMatchers("/").permitAll()
.antMatchers("/user/protected*").authenticated()
.anyRequest().permitAll().and()
// Custom filter for logging in users at "/login"
.addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
// Custom filter for authenticating users using tokens
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
// Disable resource caching
.headers().cacheControl();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
//auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
}
}
This is throwing exception the following exception:
2018-03-06 23:35:54.766 ERROR 60780 --- [nio-8080-exec-5] o.k.a.rotation.AdapterRSATokenVerifier : Didn't find publicKey for kid: w3Qn-XFRI_1uGaLs8_bJxo_hYmHSs1_-EilRV7y98G4
2018-03-06 23:35:54.778 ERROR 60780 --- [nio-8080-exec-5] o.k.a.BearerTokenRequestAuthenticator : Failed to verify token
Any idea what am I doing wrong?

Resources