hasAuthority() with Okta bearer token for Spring Cloud Gateway - spring-boot

I am having multiple downstream services to which I am routing via a spring cloud gateway service which also has okta auth. Currently I am able to route authenticate users at the gateway and return 401 for all requests without a valid bearer token. However when I try to implement role based auth, i.e. GET requests can be sent downstream for everyone group, but POST requests require 'admin' group membership in okta. This does not work as any authenticated user is currently able to make post requests. I have added claims to the access/id tokens and checked them in the Token preview section of my default Authorisation server in okta. Following is my security configuration.
#EnableWebSecurity
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests -> authorizeRequests
.antMatchers(HttpMethod.POST,"/*").hasAuthority("admin")
.anyRequest().authenticated())
.oauth2ResourceServer().jwt();
}
}
Due to only the gateway having okta auth and downstream services being protected by api token, I cannot implement preAuthorize and have to rely on httpsecurity, but I seem to be missing something

Related

Handling authentication of microservice at Gateway service in Spring boot

I am learning authentication at gateway service in a microservices architecture. What i understood is
When the client makes a request, it needs to have a valid access token
The authentication of the requests is happening at the gateway level, the authorization at the microservice application level
Request is processed if authentication is success (Jwt token is valid)
My Questions are :
Is it really needed to have authentication at gateway service as well as individual microservice ? Because its redundant to have the same logic at both places (JWT Validation)
If not (only at gateway service), how can the individual microservice can be protected if the call is not via gateway ?
Assuming that the Microservices are accessed only via the gateway, the authentication can be delegated to the Gateway that then send the informations relative to the caller to the recipient microservice(via an header for instance).
It is important to restrict the access to the microservices to the Gateway only.
This can be done at network level if there is a firewall or a router in between or via code configuring the microservice with soething like this:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/foos/**").hasIpAddress("11.11.11.11")
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.csrf().disable();
}
// ...
}
This is the whitelisting part. Then you need to verify how to pass the information of the logged in user to the microservices from the Gateway. If you need more infow about the white listing check this article: https://www.baeldung.com/spring-security-whitelist-ip-range

Handling token response in spring oauth2 client

I am using Spring Cloud Gateway as a API Gateway for our system. We would like to delegate all authentication (oauth) to that component. I was looking at the source code of Spring Oauth2 Client but I don't see any place where I can "plug in" to do what I need.
I would like to catch the moment, when the code exchange is successful and make a redirect with id_token and refresh_token in cookie or query param. We don't store any session as well - whole authentication is meant to stateless.
I am configuring SecurityWebFilterChain (security for WebFlux) like this:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(Customizer.withDefaults())
.oauth2Login();
http.csrf().disable();
return http.build();
}
I tried to use successHandler
.oauth2Login(c -> c.authenticationSuccessHandler(successHandler));, but in that moment I don't access to refresh_token (have only WebFilterExchange, Authentication in arguments) and I am not even sure how should I perform the redirect form that place.
Is there any way to achieve this?

Passing auth header to permitAll path throws 401

I have an API configured with Spring Security's permitAll():
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api").permitAll()
.and().httpBasic();
}
}
While this works, I noticed if you include an Authorization Basic header, Spring Security will still try to process and validate the auth header.
I'm using AWS API gateway for authorization. AWS will then forward the request to my application. The thing is, AWS API gateway forwards the auth header to the app.
What's the best way to solve this, do I remove the header before AWS sends the request to the app? or I configure Spring Security to disregard the auth header if an API is public?

spring 5 - Oauth2 server get current user

I have build a Spring boot oauth2 server using spring 5. The oauth server works and I can login using basic auth and username/password. I get a bearer token and I can verify the token using the /oauth/check_token url.
In the same Spring boot project I want to add an endpoint that will print out the authenticated user information so that oauth2 clients can get information over the logged in user. I created an endpoint /user and it looks like this:
#GetMapping("/user")
#ResponseBody
public Principal user(Principal user) {
return user;
}
I startup postman so that I can do the api calls and such, call /oauth/token and I receive a token. I then start a new request, set the authentication method to bearer token and fill in the received bearer token. I do a GET call to the url (http://localhost:8080/user) and it turns out the principal is always null. I know because I can debug my application in Spring tool suite and Principal is always empty. I have also tried:
OAuth2Authentication oAuth2Authentication = (OAuth2Authentication)SecurityContextHolder.getContext() .getAuthentication();
That is empty as well. How can I create an endpoint that will print the user info so that clients can set the userInfoUri property.
I have the answer. I'll post it here just in case someone else runs into this problem.
I have a ResourceServerConfigurerAdapter with the following configure in it:
public void configure(HttpSecurity http) throws Exception {
http.anonymous()
.disable()
.requestMatchers()
.antMatchers("/api/user/**").and().authorizeRequests()
.antMatchers("/user").authenticated()
// More rules here
The reason it didn't work because of the first antMatchers("/api/user/**"). I changed it into this:
http.anonymous()
.disable()
.requestMatchers()
.antMatchers("**").and().authorizeRequests()
I believe that the first antMatchers determines the path in which it forces authorizeRequests (Enabling oauth2 security in that path) so if the first path is .antMatchers("/api/user/**") after that is .antMatchers("/user") then it won't match and the /user url won't have the oauth2 security enabled.

Enabling Spring Boot Health Actuator with OAuth2 Authentication

We've seen a number of questions related to Spring Boot's Health Actuator endpoint for version 1.5+. Having gone through a number of them, we're still at a loss.
Our goals are:
To utilize Spring Boot/Spring Security's auto configuration of the security filter chaining (i.e. not have to fully implement/configure HttpSecurity)
To enable secured health access (to see a full view into the health information of the application)
To enable unsecured health access (to allow for an endpoint to function as a liveness probe in Kubernetes)
We've tried:
Using a WebSecurityConfigurerAdapter and configuring the WebSecurity object to ignore security for the /health endpoint, and then mapping a separate endpoint to the /health endpoint according to Routing multiple URLs to Spring Boot Actuator's health endpoint to hopefully enable a secured path.
Ensuring that security.oauth2.resource.filter-order=3 as was recommended in https://github.com/spring-projects/spring-boot/issues/5072. This puts the OAuth2AuthenticationProcessingFilter before the Actuator's Mvc endpoints, and allows for requests that contain pre-authenticated Authorization: Bearer ... headers (such as JWT authorizations) to be processed. However, it dictates that all requests contain authorization - otherwise, the FilterSecurityInterceptor triggers Secure object: FilterInvocation: URL: /health; Attributes: [#oauth2.throwOnError(authenticated)] and an AccessDeniedException
Utilizing Basic Authentication for /health and OAuth2 for everything else is a no-go (see Spring boot oauth2 management httpbasic authentication & https://github.com/spring-projects/spring-boot/issues/5072).
The question that we keep coming back to is how do we get:
Anonymous requests to the /health endpoint to function as unsecured
Pre-authenticated requests (i.e. those that contain pre-authenticated Authorization: Bearer ... headers) to the /health endpoint not having the appropriate authorizations or roles to function as unsecured
Pre-authenticated requests (i.e. those that contain pre-authenticated Authorization: Bearer ... headers) to the /health endpoint having the appropriate authorizations or roles to function as secured
We can easily allow any request to access /health by having something like:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring().antMatchers(HttpMethod.GET, "/health", "/info");
}
}
And that works great solely as a readiness/liveness probe. However, when the user is actually authenticated, it doesn't provide the benefit of seeing which backing services may be misbehaving.
Thanks in advance!
I faced something similar and this somewhat works:
Add a hacky access rule for actuators eg: #oauth2.clientHasRole('ROLE_CLIENT') or hasRole('ROLE_ANONYMOUS') that enables the security context to be populated for the actuator endpoints (for both authenticated and non authenticated requests) and tweak the 'sensitive' actuator endpoints configuration.
/health should return basic info for anonymous and full info for authenticated in this case, provided you enable the management security and mark it as non sensitive.
You still need to keep the filter configuration.
Try this, work for me
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests()
.antMatchers("/actuator/**","/assets/**")
.permitAll()
........
;
}

Resources