Authentication with OAuth2 in Webflux Spring - spring

I'm developing an app, in which i want to have role-based access control, unfortunately I didn't find any good example with spring webflux usage.
My oauth2.client.provider is Okta.
Here is my SecurityWebFilterChain:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers("/*").permitAll()
.pathMatchers("/admin").hasRole("admins");
}
In this article I've found that I should configure resource server. Give me a hint how to do it,please.

You'll need to use a milestone release of Spring Boot 2.1 for this to work. M3 or higher should do the trick. Add the necessary dependencies for Spring Security 5.1 OIDC support:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
Then create an Okta OIDC "Web" app and copy your settings into src/main/resources/application.yml.
spring:
security:
oauth2:
client:
provider:
okta:
issuer-uri: https://dev-737523.oktapreview.com/oauth2/default
registration:
login:
okta:
client-id: {clientId}
client-secret: {clientSecret}
scope: openid email profile
Restart your app, go to http://localhost:8080, and you should be redirected to Okta to log in. Enter valid credentials, and you'll be redirected back to your app after a successful log in.
To limit access based on roles, you'll need to create groups for your users.
Create a ROLE_ADMIN and ROLE_USER group (Users > Groups > Add Group) and add users to them. You can use the account you signed up with, or create a new user (Users > Add Person). Navigate to API > Authorization Servers, click the Authorization Servers tab and edit the default one. Click the Claims tab and Add Claim. Name it “groups” or “roles”, and include it in the ID Token. Set the value type to “Groups” and set the filter to be a Regex of ".*" (to include them all).
Then you should be able to use something like:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers("/*").permitAll()
.pathMatchers("/admin").hasAuthority("ROLE_ADMIN");
}
You should also be able to use #PreAuthorize as mentioned in this blog post.

Related

Spring boot authorization issue with fetching roles from Azure AD auth server

As per this we implemented Spring boot auth with Azure AD: https://ordina-jworks.github.io/security/2020/08/18/Securing-Applications-Azure-AD.html
Here the access token validation works fine but not showing any authorities:
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
Collection<? extends GrantedAuthority> authoritiesFromToken = authentication.getAuthorities();
System.out.println("authoritiesFromToken: " + authoritiesFromToken);
The following dependencies are used:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
Added app roles in Azure AD: https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps
Used Postman to get the Azure AD token with client credentials: https://learn.microsoft.com/en-us/rest/api/servicebus/get-azure-active-directory-token
After this run the application but the app roles set to the application is not printed.
What we need to do to get the roles as well?
we need add
scope: openid,profile
in application.yml file to get the profile information of user.
Sometimes token is an access token may not be for your app. The
roles you are looking for can be present in the id token.
Please make sure to check both id token and access token in
authentication blade or try by checking both individually.
As you were not able to access the roles in your token. Please check
if you were not assigning a resource in request parameters
along with (client_id, grant_type, etc.) . By default the resource
maybe something like 00000002-0000-0000-c000-000000000000. Also try by sending
responseType =token in the request.
Please check this Azure AD-protected Web API using Spring Boot Starter for Azure Active Directory (aaddevsup.xyz)
Add the following dependencies to your pom.xml file.
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-active-directory</artifactId>
// <version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
According to note here in Add app roles and get them from a token - Microsoft identity platform | Microsoft Docs
Note: Currently if you add a service principal to a group, and then assign an app role to that group, Azure AD does not add the roles
claim to tokens it issues. Azure AD emits a roles claim for each role
that the user or service principal has been granted individually to
the user and the user's group memberships.
The roles could also be added directly in the manifest json file.
"appRoles": [
{
"allowedMemberTypes": [
"User , Applications"
],
"description": " ",
"displayName": " ",
"id": "<Guid> ",
"isEnabled": true,
"lang": null,
"origin": "<> ",
"value": " "
},
]
If you're implementing app role business logic in an app-calling-API
scenario, you have two app registrations. One app registration is for
the app, and a second app registration is for the API. In this case,
define the app roles and assign them to the user or group in the app
registration of the API. When the user authenticates with the app and
requests an access token to call the API, a roles claim is included in
the access token. Your next step is to add code to your web API to
check for those roles when the API is called.
Check the user.read is for graph api and to get token for api for your app should have the scope exposed for api.and granted api permissions for admin consent.
To learn how to add authorization to your web API, see Protected web API: Verify scopes and app roles.
Reference :
Spring Boot Starter for Azure Active Directory developer's guide | Microsoft Docs

How to enable health in Spring Boot Actuator

I have to check whether my service / app works or not.
I've added dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.6.2</version>
</dependency>
and also tried to add management.endpoint.health.show-details: always to application.yml but it didn't help.
I tried to go to http://localhost:8080/actuator/health, http://localhost:8080/health but it returned 404 error.
You can try this code on your application.yaml. This is worked for Spring boot 2.6.7.
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: health
As you see, you have 404 both on
http://localhost:8080/actuator/health
and
http://localhost:8080/health
Reason for this is not because security is enabled, if security was enabled you will get 401 or 403.
You probably need to expose actuator endpoints in application.yaml file.
Something like this:
management:
endpoints:
web:
exposure:
include: "health,info"
And if you have security enabled, you need to write you own SecurityFilterChain implementation in which you will disable security on all Actuator endpoints, or in your case only on those that you exposed in your application.yaml file.
Example:
#Configuration
class ActuatorSecurityAutoConfiguration {
#Bean
SecurityFilterChain
surpassingActuatorSecurityFilterChain(HttpSecurity
httpSecurity) throws Exception {
return httpSecurity
.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests()
.anyRequest()
.permitAll()
.and().build();
}
}
By default Spring boot enables security for all actuator endpoints
You can disable that feature using below property
management.security.enabled=false
Henceforth, try to run the application and hit the endpoint
http://localhost:8080/actuator

401 while trying to access Swagger UI - Springdoc

I was writing spring application.I added swagger into my project but somehow It couldn't run properly.Into my project also has bearer authentication with token. Please give me a hint How I might fix this problem
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.1.44</version>
</dependency>
Actually the problem is in your security setting. All resources/endpoints are protected by default when security is present on class path. What you need to do is to expose all resource that are needed for Swagger UI publicly. To do so you have at least two options. The first is to change or create configuration like this:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring().antMatchers("swagger-ui/**", "swagger-ui**", "/v3/api-docs/**", "/v3/api-docs**");
}
By this you override whole HttpSecurity for mentioned paths means no CORS, CSRF, authorization will be checked.
The other option is to try Havelock. With the library you can expose swagger resource by one annotation.
Disclaimer: I am the founder and tech lead of the project
First of all use the last stable version.
This should help:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.4.4</version>
</dependency>
If the issue still persists, add more relevant information about your code to reproduce it.

migrate to keycloak from spring boot security

i want to migrate to keycloak from my old spring boot security app.Below is my security config.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/*", "/static/**", "/css/**", "/js/**", "/images/**").permitAll()
.antMatchers("/school-admin/*").hasAuthority("SCHOOL_ADMIN").anyRequest().fullyAuthenticated()
.antMatchers("/teacher/*").hasAuthority("TEACHER").anyRequest().fullyAuthenticated()
.anyRequest().authenticated().and()
.formLogin()
.loginPage("/login.html").defaultSuccessUrl("/loginSuccess.html")
.failureUrl("/login.html?error").permitAll().and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout.html")).logoutSuccessUrl("/login.html?logout");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
I have already installed the keycloak and it is running on port 8080.The problem I found out that, we should create role and user on keycloak admin page, But what my current system is, users and roles are on my MySQL DB. I don't want to insert the users and roles on keycloak for authentication and authorization.
Ok, obviously the first thing is a running keycloak instance, I assume this should be doable with the online documentation. We use i.e. Keycloak on a Wildfly instance.
Next step is to define a realm and at least one client in keycloak that you will use to connect to with your spring-boot application. In you application's POM you will need to add dependencies for a keylcoak adapter like i.e.
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-tomcat8-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-adapter</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
The rest can be done in your application.properties, that's the place where you configure how the adapter connects to keycloak and which parts of your application should be secured. This can look like
keycloak.realm=myrealm #realm that you have created in keycloak, contains your client
keycloak.auth-server-url=KeycloakHOST:KeycloakPort/auth # Substitute with your settings
keycloak.ssl-required=none
keycloak.resource=myclient
#keycloak.use-resource-role-mappings=true
keycloak.enable-basic-auth=true # we use basic authentication in this example
keycloak.credentials.secret=2dcf74ca-4e4f-44bf-9774-6c32c12783d3 # Secret generated for you client in keycloak
keycloak.cors=true
keycloak.cors-allowed-headers=x-requested-with,origin,content-type,accept,authorization
keycloak.cors-allowed-methods=GET,POST,DELETE,PUT,OPTIONS
keycloak.cors-max-age=3600
keycloak.expose-token=true
keycloak.bearer-only=true
keycloak.securityConstraints[0].securityCollections[0].name=adminRule
keycloak.securityConstraints[0].securityCollections[0].authRoles[0]=SCHOOL_ADMIN
keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/school-admin/*
keycloak.securityConstraints[1].securityCollections[0].name=teacherRule
keycloak.securityConstraints[1].securityCollections[0].authRoles[0]=TEACHER
keycloak.securityConstraints[1].securityCollections[0].patterns[0]=/teacher/*
That's basically all you need to do in your spring-boot application. All other endpoints not covered by the rules above remain available to all. You can find a pretty good tutorial on that here that is the longer version what I have described.

How to combine Feign and OAuth 2.0?

I'm using Spring Feign and Oauth 2.0
My application has
1 Api gateway with #EnableOAuth2Sso
2 Services with #EnableResourceServer
When I call an api of an service from the other service, I get this exception.
feign.FeignException: status 401 reading TestFeign#test(); content:
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
How to call an api of an service from the other service?
For services you can use these dependency.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.jmnarloch</groupId>
<artifactId>feign-oauth2-spring-cloud-starter</artifactId>
<version>1.0.0</version>
</dependency>
This way does not work for api gateway!!!

Resources