How to get UserGroups from KeyCloak and print them in Java - spring-boot

I have a spring-boot application which uses spring-boot-starter-security and I have integrated Keycloak to Spring Boot application for authentication / authorization purposes. I want to fetch UserGroups from KeyCloak and display them in my SpringBoot Application. Do you know a way to fetch UserGroups from KeyCloak and display them at screen? There should be an implementation to provide it ?
This is my SecurityConfiguration java :
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class KeyCloakSecurityConfig extends WebSecurityConfigurerAdapter {
private final KeycloakJwtConfig keycloakJwtConfig;
#Override
public void configure(final HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.formLogin().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/actuator/health").permitAll()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new JwtAuthoritiesExtractor())
.jwkSetUri(this.keycloakJwtConfig.jwkSetUri());
}
#Autowired
public SecurityConfiguration(final KeycloakJwtConfig keycloakJwtConfig) {
keycloakJwtConfig = keycloakJwtConfig;
}

For listing all user groups within a Keycloak realm in Java, you may use Keycloak Admin REST client library provided with Keycloak and available in public Maven repositories: groupId=org.keycloak, artifactId=keycloak-admin-client, version is/should be the same as your Keycloak server (at least the same major and minor parts). Basically, with this library, you create a Keycloak instance, then select the realm, and get the groups from there, e.g. as shown in the Server Developer guide, section Admin REST API > Example using Java and the GroupTest test case on Keycloak github (method searchAndCountGroups).
Keycloak keycloak = Keycloak.getInstance(
"http://localhost:8080/auth",
"master",
"admin",
"password",
"admin-cli");
RealmRepresentation realm = keycloak.realm("master").toRepresentation();
for (GroupRepresentation group : realm.groups().groups())
{
// Display group.getId(), group.getName(), etc.
}

Related

How to secure only swagger UI page " swagger-ui/index.html#/ " and all other API end points should not be authenticated in Spring boot

I have a requirement where I just need to secure the Swagger UI page. All other endpoints I have written in the application should not be authenticated.
For this, I am using the Spring security starter. I have the Security Config for Spring boot in place. I am trying to authenticate ("/v2/api-docs") because this is where we see all the endpoints in Swagger UI. And also I am trying to permit ("/calculator-controller/callCalculatorServiceUsingPOST") which I see in browser URL when I click on my end point Try it now button and also permitting ("/calculate") which is in my controller. To be safer, I have tried to permit all possible combinations, but no luck.
What Am I missing ???
#Configuration #EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.authorizeRequests()
.antMatchers("/v2/api-docs").authenticated()
.antMatchers("/calculator-
controller/callCalculatorServiceUsingPOST",
"calculator-controller/**", "/calculate")
.permitAll()
.and()
.httpBasic();
}
}

Adding support for multi-tenancy in Spring Boot application using Spring Security

I am new to Spring Security and Oauth2. In my Spring Boot application, I have implemented authentication with OAuth2 for one tenant. Now I am trying to multi-tenancy in my Spring Boot application. From the answer to the previous post: OAUTH2 user service with Custom Authentication Providers, I have implemented two security configurations in order to support two tenants: Tenant1 and Tenant2 as follows:
Custom OAuth2 user service is as follows:
#Component
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private UserRepository userRepository;
#Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
...
}
}
Tenant 1 security configuration is as follows:
#Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customOAuth2UserService;
public SecurityConfiguration(CustomOAuth2UserService customOAuth2UserService) {
this.customOAuth2UserService = customOAuth2UserService;
}
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority("ADMIN")
.antMatchers("/tenant1/**").authenticated()
.and()
.oauth2Login()
.userInfoEndpoint().userService(oauth2UserService());
http
.cors().disable();
}
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
return customOAuth2UserService;
}
}
Tenant 2 security configuration is as follows:
#Order(90)
#Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new AntPathRequestMatcher("/tenant2/**"))
.csrf().disable()
.authorizeRequests()
.antMatchers("/tenant2/**").hasAuthority("USER")
.and()
.httpBasic();
http
.cors().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER");
}
}
application properties are as given below:
clientApp.name=myapp
spring.security.oauth2.client.registration.keycloak.client-id=abcd
spring.security.oauth2.client.registration.keycloak.client-name=Auth Server
spring.security.oauth2.client.registration.keycloak.scope=api
spring.security.oauth2.client.registration.keycloak.provider=keycloak
spring.security.oauth2.client.registration.keycloak.client-authentication-method=basic
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
myapp.oauth2.path=https://my.app.com/oauth2/
spring.security.oauth2.client.provider.keycloak.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.keycloak.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.keycloak.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.keycloak.user-name-attribute=name
Basically, the intent of my application is B2B. So if I want to onboard a new business entity B as a tenant of my application, plugin its authentication provider, all its existing users should get authenticated seamlessly.
So, in view of the above, I have thought of the approach (though I am not sure if it's the best approach) as follows:
There can be a single endpoint for all the tenants i.e. there can be a common login page for all the users regardless of the tenant. On this login page, there can be the provision for the users to enter only email IDs.
The tenant ID can be determined from the email ID entered by the user.
Based on tenant ID, authentication provider of associated tenant ID gets invoked in order to authenticate the user of associated tenant.
On successful authentication, redirect to the home page for the associated tenant as: https://my.app.com/<tenant-id>/
In addition to the above, I would like to build a setup, where my application has quite a few, say, 40 tenants, out of which say 20 tenants use OAuth2, 10 uses basic auth and 10 uses form login.
Here in order to implement the above type of functionality, from Multi tenancy for spring security, it seems I have to support one authentication method, add tenant ID to authentication token and then create an adapter to other authentication methods, as needed.
But, in this regard, I did not find any concrete idea in any post so far on what changes should I do in the existing code base in order to achieve this.
Could anyone please help here?

SecurityContextHolder.getContext().getAuthentication() returns null

I'm developing Spring boot and security web application with authorization and resource servers enabled. I have defined a set of users with roles assigned to them and have implemented roles based access to rest endpoints. Besides that my application has straightforward UI with web pages. Those pages display the same data that is on rest. I'm trying to implement the same roles based access to pages with ResourceServerConfig#configure and my current code:
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login.jsf").permitAll()
.loginProcessingUrl("/login")
.defaultSuccessUrl("/login-successful", true)
.and().logout().permitAll();
}
This configuration works perfectly for REST controllers access with bearer token, but authorization with the login form leads to the redirect to the /login-successful and the message
Full authentication is required to access this resourceunauthorized is displayed.
The reason is that SecurityContextHolder.getContext().getAuthentication() for /login-successful request in spite it was correctly initialized in AbstractAuthenticationProcessingFilter#successfulAuthentication at the point of login form post. The same issue with other web pages in my app as well.
What should be added to the above configuration so that make it work for the REST and form login bought ?
Here is indicted that HttpSecurity configuration provided above is enough for authorization with form login to work correctly as far as .anyRequest().authenticated() should pass security context for all the resources in the application.
A similar case is described here but the reason over there is that an url was explicitly ignored in WebSecurity configurer.
The problem was in the fact that I was using deprecated #EnableResourceServer annotation that adds OAuth2AuthenticationProcessingFilter. For the form login authorization flow this is incorrect and that filter was removing authentication object from the SecurityContext. Here is indicated that OAuth2AuthenticationProcessingFilter shouldn't present in the filter chain for the form login authorization flow.
The reason why I was needed #EnableResourceServer annotation is that there are there is the bearer authentication flow in my application alongside with form login.
I replaced #EnableResourceServer annotation and ResourceServerConfigurerAdapter for the bearer authentication flow with Spring Security 5 resource server as http.oauth2ResourceServer() that is in WebSecurityConfigurerAdapter ( see here ). Finally the solution is with the following two WebSecurityConfigurerAdapter-s:
For bearer authorization flow:
#Configuration
#Order(2)
public class SecurityConfigRest extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
NimbusJwtDecoder jwtDecoder = Build custom JWT decoder;
http.csrf().disable()
.requestMatcher(new AntPathRequestMatcher("/rest/**"))
.authorizeRequests()
.mvcMatchers("/products/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt().decoder(jwtDecoder);
}`
and for the form login authorization flow:
#Configuration
#Order(1)
#EnableWebSecurity
public class SecurityConfigFormLogin extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http .requestMatcher(new AntPathRequestMatcher("/view/**"))
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/view/login").permitAll()
.defaultSuccessUrl("/view/login-successful", true)
.and().logout()
.logoutUrl("/view/perform_logout")
.logoutSuccessUrl("/view/login");
}
These two WebSecurityConfigurerAdapter-s make it possible to separate those two authorization flows.
As far as Spring Security 5 resource server supports only JWT or Opaque tokens ( see here ) it requires additional configuration. Here is a detailed description of such a configuration for Spring Security 5 resource server.

Spring boot how to have Thymeleaf web page and REST API with different authentications Schemes

Like the question said, how we can configure Spring Security to have form authentication for the Thymeleaf web page part of the project, and JWT authentication for the REST API part of the project?, because we like to have both projects on the same container and not to have to resource to external Tomcat Application Server to have the same Security Config (SSL, Ciphers, Certificates, ETC.).
So far we don't found how to do it, but if you can have a Thymeleaf and REST API on the same project i think it is possible to configure Spring Security to have to ways of authentication on the project.
You can have this behavior by adding two WebSecurityConfigurerAdapter beans as follows:
#Order(1) - /api/** protected by basic auth, in your case JWT
authentication.
#Order(2) - /website/** protected by form login, in your case
Thymeleaf login.
View docs for Spring Boot and sample code here.
#EnableWebSecurity
public class SecurityConfig {
#Configuration
#Order(1)
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("API_USER")
.and()
.httpBasic();
}
}
#Configuration
#Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/website/**").hasRole("ADMIN")
.and()
.formLogin()
.and()
.logout().permitAll()
;
}
}
}

Spring Boot - How to secured the dashboards like Hystrix for admin users

I have spring boot REST service with a basic authentication already existing. I have Hystrix integrated to short circuit external services and a dashboard to monitor the same. But how to secure Hystrix dashboard for an admin user?
Thanks,
Arun.
Configure Spring Security to enforce basic auth on /hystrix endpoint. For example:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/hystrix")
.authenticated();
}
}

Resources