Keycloak won't work with spring boot in browser, only in postman, java.lang.NoSuchMethodError: 'org.keycloak.TokenVerifier.audience(java.lang.String) - spring-boot

I am trying to authenticate with Keycloak, it does work in postman, but when I try to access the endpoints in the browser, after the login it redirects me back to the login page, and in the logs I see
Servlet.service() for servlet [dispatcherServlet] in context with path [/test-portal] threw exception [Filter execution threw an exception] with root cause
"java.lang.NoSuchMethodError: 'org.keycloak.TokenVerifier org.keycloak.TokenVerifier.audience(java.lang.String)'
How can I get to access my endpoints through the browser too? Where should I create this 'audience' method?
#KeycloakConfiguration
#EnableWebSecurity
#Order(1)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
private final KeycloakLogoutHandler keycloakLogoutHandler;
public SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) {
this.keycloakLogoutHandler = keycloakLogoutHandler;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http); //lasa-l pt postman
http.authorizeRequests().antMatchers("/infoboard/*").permitAll().and()
.authorizeRequests().anyRequest().authenticated();
http.oauth2Login()
.and()
.logout()
.addLogoutHandler(keycloakLogoutHandler)
.logoutSuccessUrl("/");
}
}
#Keycloak Configuration
keycloak.auth-server-url=https://keycloak.fh-kufstein.ac.at:8443/
keycloak.realm=BigOpenRealm
keycloak.resource=PortalMicroservices
keycloak.public-client=false
keycloak.principal-attribute=preferred_username
#keycloak.use-resource-role-mappings=false
keycloak.credentials.secret=a7VJbtIFRipKZJ78Ex9uIzOv1hikNTWx
spring.security.oauth2.client.registration.keycloak.client-id=PortalMicroservices
spring.security.oauth2.client.registration.keycloak.client-secret=a7VJbtIFRipKZJ78Ex9uIzOv1hikNTWx
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid
spring.security.oauth2.client.provider.keycloak.issuer-uri=https://keycloak.fh-kufstein.ac.at:8443/realms/BigOpenRealm
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

Related

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

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.

Springboot 2 Oauth2 cannot redirect to SSO client

I'm current working on the implementation of Springboot 2.x oauth2. But I got some tricky problems.
The project comprises both auth-server and sso-client (GitHub link is provided in the bottom). The problem is: when I entered a protected URL (eg localhost:9000/) it will be redirected to the login page configured in the auth-server. However, it won't redirect back to sso-client after successful login.
Authorization server config for auth-server:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private AuthenticationManager authenticationManager;
public AuthorizationServerConfig(AuthenticationConfiguration authenticationConfiguration) throws Exception {
this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret("secret")
.authorizedGrantTypes("authorization_code")
.scopes("all")
.autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
Security config for auth-server:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("root")
.password(passwordEncoder().encode("root"))
.roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable();
}
}
Security config for sso-client:
#Configuration
#EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.anyRequest().authenticated();
}
}
application.yml for sso-client:
auth-server: http://localhost:9090
server:
port: 9000
security:
oauth2:
client:
client-id: client
client-secret: secret
scope: all
user-authorization-uri: ${auth-server}/oauth/authorize
access-token-uri: ${auth-server}/oauth/token
resource:
token-info-uri: ${auth-server}/oauth/check_token
preferTokenInfo: false
Here is the link to this project: https://github.com/paul8263/SpringBoot2Oauth2
PS: I made it work with spring boot 1.5.8: https://github.com/paul8263/SsoDemo2
I compared the codes with Springboot2 (first link) but I barely noticed any obvious difference.
Could someone help me solve this problem by making the simple demo working? Many thanks.

Spring OAuth2 "Full authentication is required to access this resource error" when trying to access login url

I am using Spring Security OAuth2 (Spring Boot 2.0.2 + Spring Cloud Finchley) and trying to initiate an implicit login. The browser redirects me to the /login URL but I get the the error "Full authentication is required to access this resource." How do I allow the login page to be displayed but still allow all REST urls to be secured?
My config is as follows:
App.java
#SpringBootApplication
#RestController
#EnableResourceServer
#EnableAuthorizationServer
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
OAuth2Config.java
#Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("XXXXX")
.secret("XXXXX")
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
WebSecurityConfigurer.java
#Configuration
#Order(-20) // EDIT
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("XXXXX"). password("XXXXXX").roles("USER");
}
// EDIT
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().permitAll()
.and().httpBasic().and()
.requestMatchers()
//specify urls handled
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.antMatchers("/fonts/**", "/js/**", "/css/**")
.and()
.authorizeRequests()
.antMatchers("/fonts/**", "/js/**", "/css/**").permitAll()
.anyRequest().authenticated();
}
}
}

Spring security oauth2 always returning 403

I have a Spring boot app serving Rest endpoints which I'm securing using Spring security and Oauth2.
I want to secure all my endpoints except the endpoints used to authenticate, to create an account or some info stuff.
The security configuration is like this :
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private MongoTokenStore tokenStore;
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
//clients.withClientDetails(clientDetailsService);
clients.inMemory().withClient("app").secret("password")
.accessTokenValiditySeconds(30000).authorizedGrantTypes("password", "refresh_token")
.refreshTokenValiditySeconds(300000000)
.scopes("read");
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager)
.pathMapping("/oauth/confirm_access", "/access_confirmation");
}
#Bean
public TokenStore tokenStore() {
return this.tokenStore;
}
}
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserRepository userRepository;
#Autowired
private SecurityContextService securityContextService;
#Autowired
private MongoTemplate mongoTemplate;
#Bean
public MongoUserDetailsManager mongoUserDetailsManager() throws Exception {
return new MongoUserDetailsManager(userRepository, securityContextService, authenticationManagerBean(), mongoTemplate);
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManagerBean())
.userDetailsService(mongoUserDetailsManager());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/login", "/oauth/authorize", "/oauth/token", "/server/version", "/clients/register").permitAll()
.and().csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.disable();
}
}
I can access to token endpoint to get my access_token, but I want to access to other secured endpoints using this access_token (by adding the Authorization:Bearer {access_toke} to the header), I always get HTTP 403.
Did I miss something? I'm not supposed as authorized if I add the Authorization header?
My Controllers are only annotated with these #RestController, #CrossOrigin
and #RequestMapping("/url")
There are 2 types of security configurations in case of OAuth security(as far as urls security is concerned) in Spring.
1. Basic Security Configuration
This class should implement WebSecurityConfigurerAdapter. It will handle all those requests coming without "Bearer" token type(urls that shouldn't be oauth protected).
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserRepository userRepository;
#Autowired
private SecurityContextService securityContextService;
#Autowired
private MongoTemplate mongoTemplate;
#Bean
public MongoUserDetailsManager mongoUserDetailsManager() throws Exception {
return new MongoUserDetailsManager(userRepository, securityContextService, authenticationManagerBean(), mongoTemplate);
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManagerBean())
.userDetailsService(mongoUserDetailsManager());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/login", "/oauth/authorize", "/oauth/token", "/server/version", "/clients/register").permitAll()
.and().csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.disable();
}
}
2. Resource Server Configuration(OAuth Specific)
This class is responsible for handling all those requests coming with authorization header of type Bearer. It should be extended from ResourceServerConfigurerAdapter class. Here you should mention all those urls with security configurations that you like to be oauth protected.
#Configuration
#EnableResourceServer
public class OAuthResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/resources-to-be-protected/**").and().authorizeRequests()
.antMatchers("/resources-to-be-protected/**").access("#oauth2.isClient()");
}
}

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