Spring Boot Keycloak- Bearer-only - for backend service not working - spring-boot

I am trying to secure my rest api and frontend using Keycloak.
Frontend is based on Angular 4.
Backend is rest api built using Spring boot.
I have created two client in keycloak admin console,in same realm(testRealm).
front-end client's ACCESS-TYPE is "Public".
Backend client(backendService) ACCESS-TYPE is "Bearer-only".
Front-End is working fine and displays tabs based on roles.
When I try to hit/access backend service call, it gives me below error in browser console-
Failed to load http://localhost:9099/api/transaction
Response for preflight has invalid HTTP status code 401.
Below is the config I am using to secure rest api.
application.properties
server.contextPath=/test
server.port=9090
keycloak.realm: testRealm
keycloak.bearer-only: true
keycloak.auth-server-url: http://localhost:8080/auth
keycloak.ssl-required: external
keycloak.resource: backendService
keycloak.use-resource-role-mappings: true
keycloak.confidential-port: 0
keycloak.credentials.secret=dc04c236-d2b9-560e-b6b2-efa2064b2386
ApiSecurityConfig.java(Spring boot security config for keycloak adapter)
#Configuration
#EnableWebSecurity
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class ApiSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#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();
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
/**
* Api request URI and their mapping roles and access are configured in this
* method.This is method from spring security web configuration Override this
* method to configure the HttpSecurity.
*
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/api/*")
.hasRole("admin")
.anyRequest()
.permitAll();
}
}
I have verified the keclaok properties in application.properties file,by cross checking the installation JSON file from Keycloak server,which looks like below.
Installation JSON file taken from the Keycloak server
{
"realm": "testRealm",
"bearer-only": true,
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "backendService",
"use-resource-role-mappings": true,
"confidential-port": 0
}
I have checked keycloak.credentials.secret and its exactly the same what I am using,still it is not allowing access to backend and giving
Unauthorized, status=401
Not sure,If I am missing something,any help is much appreciated..

Related

External keycloak server and Spring boot app behind reverse proxy - redirect to root context after success login

My spring boot service is working behind reverse proxy and secured by external keycloak server.
After successful login at Keycloak server it redirects me to my service and then I get redirect to root of context path instead of initial url.
So request chain is looks like:
initial url: http://~HOSTNAME~/~SERVICE-NAME~/rest/info/654321
and redirects:
http://~HOSTNAME~/~SERVICE-NAME~/rest/sso/login
https://ext-keycloak.server/auth/realms/test/protocol/openid-connect/auth?response_type=code&client_id=dev&redirect_uri=http%3A%2F%2F~HOSTNAME~%2F~SERVICE-NAME~%2Frest%2Fsso%2Flogin&state=60ebad0d-8c68-43cd-9461&login=true&scope=openid
http://~HOSTNAME~/~SERVICE-NAME~/rest/sso/login?state=60ebad0d-8c68-43cd-9461&session_state=074aaa0d-4f72-440e&code=a8c92c50-70f8-438c-4fe311f0b3b6.074aaa0d-440e-8726.8166b689-bbdd-493a-8b8f
http://~HOSTNAME~/~SERVICE-NAME~/rest/ - I have no handlers here and getting error.
First problem was that application generated wrong redirect uri for keycloak. All services are in kubernetes cluster and have urls like: http://~HOSTNAME~/~SERVICE-NAME~/rest (where '/rest' is context path).
~SERVICE-NAME~ part is used to locate service in cluster and application gets request without this prefix. But proxy adds header X-Original-Request with original url and I decided to use it (unfortunately I can't change configuration of proxy and keycloak servers). I made filter to use header value to generate correct redirect uri by copy-pasting from Spring's org.springframework.web.filter.ForwardedHeaderFilter. Now it generates correct redirect_uri but I'm getting wrong redirect at the end as described above.
How can I get redirect to initial page in this case?
Spring security config:
#EnableWebSecurity
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
private final PermissionConfig permissionConfig;
#Autowired
public SecurityConfig(PermissionConfig permissionConfig) {
this.permissionConfig = permissionConfig;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new NullAuthoritiesMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
var urlRegistry = http.authorizeRequests()
.antMatchers("/actuator/**")
.permitAll()
.antMatchers("/info/**")
.hasAnyAuthority(permissionConfig.getRoles().toArray(new String[0]));
}
#Bean
public FilterRegistrationBean<OriginalUriHeaderFilter> originalUriHeaderFilter() {
OriginalUriHeaderFilter filter = new OriginalUriHeaderFilter();
FilterRegistrationBean<OriginalUriHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
}
spring keycloak config (yaml)
keycloak:
auth-server-url: 'https://ext-keycloak.server/auth/'
realm: test
ssl-required: NONE
resource: dev
credentials:
secret: 'hex-value'
confidential-port: 0
disable-trust-manager: true
For future searchs, an another approach is to use the KeycloakCookieBasedRedirect.createCookieFromRedirectUrl method, that includes a cookie for keycloak redirect.
Uh, the problem was with service prefix not with Keycloak.
When I tryed to get page Spring set JSESSIONID cookie with path=/rest, stored request to session and redirected me to Keycloak. After login Spring couldn't find session and redirected me to root context because browser didn't provide JSESSIONID cookie for path /~SERVICE-NAME~/rest !!
By default Spring sets cookie path=server.servlet.contextPath. All I've done just added cookie path to application.yaml:
server:
port: 8080
servlet:
contextPath: /rest
session:
cookie:
path: /
Spring Server Properties
Spring DebugFilter is quite useful thing

Service-to-service communication with feign in spring security 5

in fact the spring-oauth project turn into maintenance mode we trying migrate our application into pure spring security 5 which support resource server configuration as well.
Our actual resource server configuration looks like this:
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(
authorizeRequests -> {
authorizeRequests.antMatchers("/api/unsecured/**").permitAll();
authorizeRequests.anyRequest().authenticated();
}
);
}
#Bean
#ConfigurationProperties(prefix = "security.oauth2.client")
public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
return new ClientCredentialsResourceDetails();
}
#Bean
public TokenStore jwkTokenStore() {
return new JwkTokenStore("http://localhost:8080/...", new JwtAccessTokenConverter());
}
#Bean
public RequestInterceptor oauth2FeignRequestInterceptor(){
return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), clientCredentialsResourceDetails());
}
#Bean
public OAuth2RestTemplate clientCredentialsRestTemplate() {
return new OAuth2RestTemplate(clientCredentialsResourceDetails());
}
}
and these properties:
security:
oauth2:
client:
client-id: service-id
client-secret: secret
access-token-uri: http://localhost:8081/oauth/token
This resource server is configured to work with jwt token. To verify token uses rsa public key from link passes to jwkstore. It is also able call another resource server with Feign.
And this is new configuration:
#Configuration
static class OAuth2ResourceServerConfig extends WebSecurityConfigurerAdapter {
private final JwtDecoder jwtDecoder;
ResourceServerConfiguration(JwtDecoder jwtDecoder) {
this.jwtDecoder = jwtDecoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests -> authorizeRequests
.antMatchers("/public/unsecured/**").permitAll()
.anyRequest().authenticated())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer
.jwt(jwtConfigurer -> {
jwtConfigurer.decoder(NimbusJwtDecoder.withJwkSetUri("http://localhost:8080/...").build());
jwtConfigurer.jwtAuthenticationConverter(tokenExtractor());
})
);
}
This configuration works fine to decode and verify tokens, but Feign doesn't work. Previous configuration with spring oauth2 supports Oauth2 feign interceptor which call authorization server to get its own access token. But I don't know how to configure this in spring security 5. This is flow which I need:
frontend client call spring resource server A with token
resource server A need data from resource server B
resource server A call authorization server to get access token with client_credentials grant type
resource server A call resource server B with its access token set to request header by feign
resource server A return all data to frontend client
Can you tell me how to configure 3. and 4. step in spring security 5 without spring's oauth project? Thank you.

Spring Boot I can't switch keycloak and basic authentication

NB: I'm using Spring Boot 2.1.10 and Keycloak 6.0.1
I wish I could choose between basic authentication and SSO at launch time for a web application (MVC).
So I first integrated Spring Security + Keycloak with keycloak-spring-boot-starter
#SpringBootApplication
#EnableWebSecurity
public class KcApplication {
public static void main(String[] args) {
SpringApplication.run(KcApplication.class, args);
}
}
Then I defined a "sso" Spring profile and a default config:
application.properties goes like this:
spring.application.name=#artifactId#
server.port: 8081
keycloak.enabled=false
spring.main.allow-bean-definition-overriding: true
and application-sso.yml goes like this:
keycloak:
enabled: true
auth-server-url: http://localhost:8180/auth
realm: SpringBootRealm
resource: spring-app
credentials:
secret: 0c8940a4-2065-4810-a366-02877802e762
principal-attribute: preferred_username
Then I got two different security configurers:
#Configuration #Profile("!sso")
public class BasicAuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers("/customers*").authenticated()
.anyRequest().permitAll()
.and().httpBasic() //DEBUG can't force
.and().logout().logoutSuccessUrl("/");
}
}
#Configuration #Profile("sso")
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class KeycloakAuthConfig extends KeycloakWebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers("/customers*").authenticated()
.anyRequest().permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider authProvider = keycloakAuthenticationProvider();
authProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(authProvider);
}
#Bean
public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
}
Everithing launches smoothly in both cases, and the "sso" profile behaves rightly: entering the /consumers path first turns to a redirection to Keycloak and coming back when authenticated.
But I can't get the default profile to log me in. When entering /consumers I get an anonymousUser, not being asked to form login.
I guess that the issue comes from something I missed, so I put above as many things as possible. Does anyone knows why I can't login, despite the fact that the right configurer was run at debug?
Thank you
Well, it took the weekend for the session to be reset, then it worked!
Proof that it's probably the logout that bugs instead... I'm not even sad :-(

How to scale horizontally a spring-boot oauth2 server with JDBC implementation

I have a spring boot oauth2 server that uses a JDBC implementation. It is configured as an authorization server with #EnableAuthorizationServer.
I'd like to scale that application horyzontally but it doesn't seem to work properly.
I can connect only if I have one instance (pods) of the server.
I use autorisation_code_client grant from another client service to get the token.
So first the client service redirect the user to the oauth2 server form, then once the user is authenticated he is supposed to be redirect to the client-service with a code attached to the url, finally the client use that code to request the oauth2 server again and obtain the token.
Here the user is not redirected at all if I have several instance of the oauth2-server. With one instance it works well.
When I check the log of the two instances in real time, I can see that the authentication works on one of them. I don't have any specific error the user is just not redirected.
Is there a way to configure the oauth2-server to be stateless or other way to fix that issue ?
Here is my configuration, the AuthorizationServerConfigurerAdapter implementation.
#Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource oauthDataSource() {
return DataSourceBuilder.create().build();
}
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Bean
public JdbcClientDetailsService clientDetailsSrv() {
return new JdbcClientDetailsService(oauthDataSource());
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(oauthDataSource());
}
#Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(oauthDataSource());
}
#Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(oauthDataSource());
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Bean
#Primary
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setTokenEnhancer(tokenEnhancer());
return tokenServices;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsSrv());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)
.approvalStore(approvalStore())
//.approvalStoreDisabled()
.authorizationCodeServices(authorizationCodeServices())
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancer());
}
}
The main class
#SpringBootApplication
#EnableResourceServer
#EnableAuthorizationServer
#EnableConfigurationProperties
#EnableFeignClients("com.oauth2.proxies")
public class AuthorizationServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthorizationServerApplication.class, args);
}
}
The Web Security Configuration
#Configuration
#Order(1)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return new JdbcUserDetails();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception { // #formatter:off
http.requestMatchers()
.antMatchers("/",
"/login",
"/login.do",
"/registration",
"/registration/confirm/**",
"/registration/resendToken",
"/password/forgot",
"/password/change",
"/password/change/**",
"/oauth/authorize**")
.and()
.authorizeRequests()//autorise les requetes
.antMatchers(
"/",
"/login",
"/login.do",
"/registration",
"/registration/confirm/**",
"/registration/resendToken",
"/password/forgot",
"/password/change",
"/password/change/**")
.permitAll()
.and()
.requiresChannel()
.anyRequest()
.requiresSecure()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login.do")
.usernameParameter("username")
.passwordParameter("password")
.and()
.userDetailsService(userDetailsServiceBean());
} // #formatter:on
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceBean()).passwordEncoder(passwordEncoder());
}
}
Client side the WebSecurityConfigurerAdapter
#EnableOAuth2Sso
#Configuration
public class UiSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers(
"/",
"/index.html",
"/login**",
"/logout**",
//resources
"/assets/**",
"/static/**",
"/*.ico",
"/*.js",
"/*.json").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), SessionManagementFilter.class);
}
}
the oauth2 configuration properties
oauth2-server is the service name (load balancer) on kubernetes and also the server path that is why it appears twice.
security:
oauth2:
client:
clientId: **********
clientSecret: *******
accessTokenUri: https://oauth2-server/oauth2-server/oauth/token
userAuthorizationUri: https://oauth2.mydomain.com/oauth2-server/oauth/authorize
resource:
userInfoUri: https://oauth2-server/oauth2-server/me
Here an important detail, the value of userAuthorizationUri is the address to access the oauth2-server from the outside of the k8s cluster. The client-service send back that address into the response with a 302 http code if the user is not connected and tries to access to the /login path of the client-service. then the user is redirected to the /login path of the oauth2-server.
https://oauth2.mydomain.com target an Nginx Ingress controller that handle the redirection to the load balancer service.
Here is a solution to this problem. It's not a Spring issue at all but a bad configuration of the Nginx Ingress controller.
The authentication process is done in several stages :
1 - the user clic on a login button that target the /login path of the client-server
2 - the client-server, if the user is not authenticated yet, send a response to the
browser with a 302 http code to redirect the user to the oauth2-server, the value of
the redirection is composed with the value of the
security.oauth2.client.userAuthorizationUri property
and the redirection url that will be used by the browser to allow the client-server to get the Token once the user is authenticated.
That url look like this :
h*tps://oauth2.mydomain.com/oauth2-server/oauth/authorize?client_id=autorisation_code_client&redirect_uri=h*tps://www.mydomain.com/login&response_type=code&state=bSWtGx
3 - the user is redirected to the previous url
4 - the oauth2-server send a 302 http code to the browser with the login url of the
oauth2-server, h*tps://oauth2.mydomain.com/oauth2-server/login
5 - the user submit his credentials and the token is created if they are correct.
6 - the user is redirected to the same address as at the step two, and the oauth-server
add informations to the redirect_uri value
7 - the user is redirected to the client-server. The redirection part of the response look like this :
location: h*tps://www.mydomain.com/login?code=gnpZ0r&state=bSWtGx
8 - the client-server contact the oauth2-server and obtain the token from the code and the state that authenticates it. It doesn't matter if the instance of the oauth2
server is different than the one used by the user to authenticate himself. Here the
client-server use the value of security.oauth2.client.accessTokenUri to get the
token, this is the internal load balancing service address that targets the oauth2 server
pods, so it doesn't pass through any Ingress controller.
So at the steps 3 to 6 the user must communicate with the same instance of the oauth2-server throught the Ingress controller in front of the load balancer service.
Its is possible by configuring the Nginx Ingress controller with a few annotations :
"annotations": {
...
"nginx.ingress.kubernetes.io/affinity": "cookie",
"nginx.ingress.kubernetes.io/session-cookie-expires": "172800",
"nginx.ingress.kubernetes.io/session-cookie-max-age": "172800",
"nginx.ingress.kubernetes.io/session-cookie-name": "route"
}
That way we ensure that the user will be redirected to the same pods/instance of the oauth2-server during the authentication process as long he's identified with the same cookie.
The affinity session mecanism is a great way to scale the authentication server and also the client-server. Once the user is authenticated he will always use the same instance of the client and keep his session informations.
Thanks to Christian Altamirano Ayala for his help.
By default an in-memory TokenStore is used.
The default InMemoryTokenStore is perfectly fine for a single server
If you want multiple pods, you probably should go for JdbcTokenStore
The JdbcTokenStore is the JDBC version of the same thing, which stores token data in a relational database. Use the JDBC version if you can share a database between servers, either scaled up instances of the same server if there is only one, or the Authorization and Resources Servers if there are multiple components. To use the JdbcTokenStore you need "spring-jdbc" on the classpath.
Source Spring Security: OAuth 2 Developers Guide

Keycloak- Angular-Rest Integration - Roles are always empty

When I read about integrating keycloak with Angular+REST application, mostly I see an approach having two clients, one public and one bearer-only. Is this the best solution or can I use a single confidential client for both application. I read that using confidential client for javascript is not the best way to do as there is no way to keep the secret hidden in javascript.
Also, after integrating keycloak to both rest and UI project using the two clients approach, authentication seems to be working. But I am not getting any roles in the rest side. I am using spring security adapter and springboot 1.5.18 for the backend. My keycloak server version is 3.4.12 and keycloak spring adapter version is 3.4.3. Keycloak configuration files are also provided below.
keycloak.json (Angular project)
{
"realm": "dev",
"auth-server-url": "https://<keycloakserver> /auth",
"resource": "frontend-dev",
"public-client": true,
"use-resource-role-mappings": true,
"confidential-port": 0,
"ssl-required": "external",
"disable-trust-manager": true
}
application.properties (springboot)
keycloak.realm=dev
keycloak.bearer-only=true
keycloak.auth-server-url=https:// <keycloakserver> /auth
keycloak.resource= backend-dev
keycloak.use-resource-role-mappings=true
keycloak.credentials.secret=222-3333-4444-5555
#development only properties
keycloak.ssl-required=external
keycloak.disable-trust-manager=true
Keycloak Java configuration
#KeycloakConfiguration
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
/**
* 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);
}
/**
* Defines the session authentication strategy.
*/
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
#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;
}
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public AccessToken accessToken() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
return ((KeycloakSecurityContext)((KeycloakAuthenticationToken) request.getUserPrincipal()).getCredentials())
.getToken();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().antMatchers("/**").permitAll();
}
}
To protect a rest resource used the annotation
#RolesAllowed(“Name of the role”)
Even after assigning a client role to the user it was throwing 403-Access denied error
I also tried to get the roles manually using the code
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.getAuthentication().getAuthorities();
But it was always returning an empty array.
I was able to finally solve the issue. Problem was with the missing Scope configuration in the frontend keycloak client.
For all the clients full scope was turned off due to security reason. Because of that unless we set explicitly in the client scope configuration of the frontend client to include the backend client roles, it wont be part of the token.

Resources