Unable to bypass spring security filter chain for specific endpoints with the permitAll() and web.ignoring() methods - spring-security

I'm using Spring Security for Single Sign On implementation. I've added the Spring Security filter chain in my application with annotations, and not in the web.xml.
I want the Spring Security filter chain to only apply to the /saml URLs, and I don't want the /myapi requests to go through Spring Security. But my /myapi requests are still going through the Spring Security filter chain, and Spring's filters.
Here's my code for Spring Security filter registration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.addFilterBefore(new SamlExtensionUrlForwardingFilter(), DisableEncodeUrlFilter.class)
.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/saml/*").authenticated()
.antMatchers("/myapi/*").permitAll()
)
.saml2Login()
.successHandler(new MyAuthenticationSuccessHandler());
return http.build();
}
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/myapi/*");
}
With the webSecurityCustomizer(), I tried to exclude the /myapi/ endpoints from the Spring Security workflow, and bypass the Spring Security filter chain and other Spring Security filters for the endpoint.
Additionally, I tried to get the same results with the following as well, assuming that permitAll() would mean Spring Security authorization is not required for the /myapi requests:
.antMatchers("/myapi/*").permitAll()
Neither of the above two worked, and the /myapi requests still go through Spring'a filters like DisableEncodeUrlFilter, CsrfFilter etc. Ideally, I want only /saml/* requests to go through the Spring Security filter chain and Spring related filters.

Related

How to use custom auth header with spring boot oauth2 resource server

I'm configuring spring cloud api gateway to support several security chains. To do that I'm using several security filter chains which triggered on specific security header presence:
The legacy one which already use Authorization header
And new implementation, that integrated with external idp. This solution utilize resource service capabilities. And for this chain I'd like to use, lets say "New-Auth" header.
In case I tune my current setup to trigger second (idp) chain on Authorization header presence (and make call with IDP token), then everything works fine. This way security chain validates token that it expect in Authorization header against idp jwk. But this header is already reserved for legacy auth.
I guess I need a way to point spring resource server chain a new header name to look for.
My security dependencies:
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
My configuration
#EnableWebFluxSecurity
public class WebSecurityConfiguration {
// ...
#Bean
#Order(1)
public SecurityWebFilterChain iamAuthFilterChain(ServerHttpSecurity http) {
ServerWebExchangeMatcher matcher = exchange -> {
HttpHeaders headers = exchange.getRequest().getHeaders();
List<String> strings = headers.get(SurpriseHeaders.IDP_AUTH_TOKEN_HEADER_NAME);
return strings != null && strings.size() > 0
? MatchResult.match() : MatchResult.notMatch();
};
http
.securityMatcher(matcher)
.csrf().disable()
.authorizeExchange()
.pathMatchers(navigationService.getAuthFreeEndpoints()).permitAll()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer(OAuth2ResourceServerSpec::jwt)
.oauth2ResourceServer().jwt().jwkSetUri(getJwkUri())
.and()
.and()
.addFilterAt(new LoggingFilter("idpAuthFilterChain"), SecurityWebFiltersOrder.FIRST)
.addFilterAfter(new IdpTokenExchangeFilter(authClientService), SecurityWebFiltersOrder.AUTHENTICATION)
;
return http.build();
}
}
Dirty solution:
We can add some filter to edit request and duplicate incoming "New-Auth" header as an "Authorization" header at a beginning of security filter chain.
Looks like it works, but I believe it should be a better way.
You can specify a ServerAuthenticationConverter to your oauth2ResourceServer configuration, like so:
http
.oauth2ResourceServer((resourceServer) -> resourceServer
.bearerTokenConverter(customBearerTokenAuthenticationConverter())
.jwt()
);
ServerAuthenticationConverter customBearerTokenAuthenticationConverter() {
ServerBearerTokenAuthenticationConverter tokenAuthenticationConverter = new ServerBearerTokenAuthenticationConverter();
tokenAuthenticationConverter.setBearerTokenHeaderName("New-Auth");
return tokenAuthenticationConverter;
}
thats for the servlet stack, the other reply you can see is for the reactive stack
#Bean
BearerTokenResolver bearerTokenResolver() {
DefaultBearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION);
return bearerTokenResolver;
}
see reference here : https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/bearer-tokens.html

Spring Cloud Security: How to support multiple Identity Provider for Oauth2 Resource Server / OAuth2 Authorization Server?

I want to build a spring cloud infrastructure with several oauth2 resource servers supporting multiple identity provider (like google, facebook, github, ....) and also one self implemented authorization mechanism.
Oauth2 Resource Server example
Security config
#Configuration
#EnableWebSecurity
#EnableResourceServer
public class Oauth2ResourceServerConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
}
}
application.yml
security:
oauth2:
resource:
user-info-uri: https://api.github.com/user
As you can see this example is using github as identity provider and its working fine.
Oauth2 Authorization Server example:
Config
#Configuration
#EnableAuthorizationServer
public class Oauth2AuthorizationServerConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}user").roles("USER")
.and()
.withUser("admin").password("{noop}admin").roles("USER", "ADMIN");
}
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
}
application.yml
security:
oauth2:
client:
client-id: user
client-secret: user
authorized-grant-types: password,client_credentials,authorization_code,refresh_token
scope: read,write
I can change my oauth2 resource server to use the authorization server:
security:
oauth2:
resource:
#user-info-uri: https://api.github.com/user
user-info-uri: http://${AUTH_HOST:localhost}:${AUTH_PORT:9000}/user
And it works just fine.
But what do I have to do if I now want to use both of them, github and my own authorization server?
Do I simply need a different configuration in my Oauth2 resource server to provide multiple user-info-uri or do I have to do more?
Can I extend my own authorization service to support github & co?
I would prefer not to use Auth0 and Co because I really dont like to outsource the most important part of my application: security. Even though I would like to try it. But I could not find any working example so far for auth0 + spring cloud gateway + token authentication for all underlying services.

Spring security 5 and OAuth2 - Success Handler not being invoked and after login it is going into infinite loop

I am trying to migrate spring security to latest version (spring security 5.2.2 and Cloud OAuth2). As #EnableOAuth2Client is in maintenance mode now, I am trying to use http.oauth2Login() with customization for success handler. Following is my security class -
#Configuration
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login().successHandler(new SimpleUrlAuthenticationSuccessHandler("<url to redirect>"));
}
}
I have registered client with name as 'custom'. Following is the flow in the browser -
http://localhost:9000/oauth2/authorization/custom -> IDP's login page and successful login -> Get Authorization code on URL http://localhost:9000/login/oauth2/code/custom -> it again goes to http://localhost:9000/oauth2/authorization/custom and infinite loop. Successhandler is not invoked. Also, I don't see access token generation in logs.
I have tried many things in last two days like tweaking security config etc but nothing works.
Any help is appreciated.
The problem was with user info endpoint. My IDP has not exposed user info endpoint while it is mandatory in spring oauth2 where the request was failing. I needed to override the default OAuth2UserService implementation to resolve the issue.

Spring WebFlux 5.0.RELEASE and Spring Security 5.0.M5

I am trying to build a web service using WebFlux. When I tried to configure Spring Security with Spring WebFlux, the requests are not intercepted by Spring Security. My Spring Security config is:
#EnableWebFlux
#EnableWebFluxSecurity
#Configuration
public class WebConfig {
#Bean
public MapUserDetailsRepository userDetailsRepository() {
UserDetails cust =
User.withUsername("user1").password("password")
.roles("USER").build();
UserDetails admin =
User.withUsername("admin1").password("password")
.roles("ADMIN").build();
return new MapUserDetailsRepository(cust, admin);
}
#Bean
public SecurityWebFilterChain springWebFilterChain(
HttpSecurity httpSecurity) {
return httpSecurity.authorizeExchange().anyExchange().
authenticated().and().build();
}
}
One way is to use method security. You need to add #EnableReactiveMethodSecurity to your configuration class and then secure handler component methods with annotations, such as #PreAuthorize("isAuthenticated()")
This is how I managed to get this working with Spring Boot 2.0.0.M4, but again this may depend on what kind of request handling you are doing.
This is an issue when using Spring Security with WebFlux. The workaround is posted here: https://jira.spring.io/browse/SPR-16144

Spring Session and Spring Security

I have questions on the following areas: spring-session and spring-security.
Spring Session
I have a application protected with Spring Security through basic in-memory authentication as provided in the example sample.
I see spring is creating session id's even the authentication is not successful, meaning I am seeing x-auth-token in my response header as well in the Redis DB even if I don't supply basic authentication credential details.
How do we avoid creating sessions for authentication failures?
Spring Security
Want to use spring security to protect resources assuming spring session creates session only for the protected resources.
Assuming a Signin API (/signin - HTTP Post) validates (username & password) credentials against a third-party REST API .
Once the external API validates the credentials, how do I update the spring security context on the successful authentication?
Access to other secured resources with the x-auth-token needs to be validated and based on the information access to the secured resource should be provided.
Do we need to have Spring Security in this case or shall I use a basic filter and spring session? What is recommended?
Typically it would be best to break your questions into multiple StackOverflow questions since you are more likely to find someone that knows the answer to a single question than both.
How do we avoid creating sessions for authentication failures ?
By default Spring Security will save the last unauthenticated request to session so that after you authenticate it can automatically make the request again. For example, in a browser if you request example.com/a/b/c and are not authenticated, it will save example.com/a/b/c to the HttpSession and then have the user authenticate. After you are authenticated, it will automatically give you the result of example.com/a/b/c. This provides a nice user experience so that your users do not need to type the URL again.
In the case of a REST service this is not necessary since the client would remember which URL needs to be re-requested. You can prevent the saving by modifying the configuration to use a NullRequestCache as shown below:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.requestCache()
.requestCache(new NullRequestCache())
.and()
.httpBasic();
}
You can provide custom authentication by providing your own AuthenticationProvider. For example:
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
public class RestAuthenticationProvider implements AuthenticationProvider {
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String username = token.getName();
String password = (String) token.getCredentials();
// validate making REST call
boolean success = true;
// likely your REST call will return the roles for the user
String[] roles = new String[] { "ROLE_USER" };
if(!success) {
throw new BadCredentialsException("Bad credentials");
}
return new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(roles));
}
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
You can then configure your RestAuthenticationProvider using something like this:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
#Bean
public RestAuthenticationProvider restAuthenticationProvider() {
return new RestAuthenticationProvider();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, AuthenticationProvider provider) throws Exception {
auth
.authenticationProvider(provider);
}
}
Session IDs are getting stored in Redis even when authentication fails.
Rob's answer of setting NullRequestCache didn't work for me. That is, there were redis entries even after setting request cache to NullRequestCache. To make it work, I had to use an authentication failure handler.
http.formLogin().failureHandler(authenticationFailureHandler()).permitAll();
private AuthenticationFailureHandler authenticationFailureHandler() {
return new AuthenticationFailureHandler();
}
public class AuthenticationFailureHandler
extends SimpleUrlAuthenticationFailureHandler {
}
Note that the failure handler does nothing but extend the default handler explicitly. It returns a 401 in case of failure. If you were doing redirects, you can configure it in the handler easily.

Resources