OidcClientInitiatedLogoutSuccessHandler not being called with non Spring Boot application - spring

Configured the logoutSuccessHandler and it works fine with a Spring Boot application. But the request to logout is not being sent to the provider with a non Spring Boot application.
Using version 5.6.2 of Spring Security
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated())
.httpBasic()
.and().logout(logout ->
logout
.invalidateHttpSession(true)
.logoutSuccessHandler(oidcLogoutSuccessHandler())
)
.oauth2Login(oauth2 -> oauth2
.clientRegistrationRepository(clientRegistrationRepository())
.userInfoEndpoint().oidcUserService(oidcUserService()));
}
public OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler() {
OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler =
new OidcClientInitiatedLogoutSuccessHandler(this.clientRegistrationRepository());
return oidcLogoutSuccessHandler;
}

Related

Spring Security 6 configuration with multiple security configs

I'm trying to setup my Spring Boot 3.0 / Spring Security 6 app with multiple security configs.
only /oauth/token should use/allow/enforce basic auth
all other endpoints will use/allow/enforce bearer auth
The issue I'm running into is that if I send a GET request to /test with the header Authorization: Basic xxx the basic auth filter is still picking it up.
This is what I have so far. The bearer filter isn't implemented yet, but for the sake of this question, let's assume all other endpoints should be wide open instead. How can I get them to bypass the basic auth filter if a user passes in basic auth header?
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(CsrfConfigurer::disable)
.authorizeHttpRequests()
.requestMatchers("/oauth/token").authenticated()
.anyRequest().permitAll()
.and()
.httpBasic(Customizer.withDefaults());
return http.build();
}
Like this one:
private static final String[] RESOURCE_ARGS = new String[]{
"/test/**"
};
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers(RESOURCE_ARGS).permitAll();
http
.csrf(CsrfConfigurer::disable)
.authorizeHttpRequests()
.requestMatchers("/oauth/token").authenticated()
.anyRequest().permitAll()
.and()
.httpBasic(Customizer.withDefaults());
....
}

Spring Security in Spring Boot 3

I'm currently in the process of migrating our REST application from Spring Boot 2.7.5 to 3.0.0-RC2. I want everything to be secure apart from the Open API URL. In Spring Boot 2.7.5, we used to do this:
#Named
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/openapi/openapi.yml").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
and it worked fine. In Spring Boot 3, I had to change it to
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests
.requestMatchers("/openapi/openapi.yml").permitAll()
.anyRequest()
.authenticated())
.httpBasic();
return http.build();
}
}
since WebSecurityConfigurerAdapter has been removed. It's not working though. The Open API URL is also secured via basic authentication. Have I made a mistake when upgrading the code or is that possibly an issue in Spring Boot 3 RC 2?
Update
Since most of the new API was already available in 2.7.5, I've updated our code in our 2.7.5 code base to the following:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests((requests) -> requests
.antMatchers(OPTIONS).permitAll() // allow CORS option calls for Swagger UI
.antMatchers("/openapi/openapi.yml").permitAll()
.anyRequest().authenticated())
.httpBasic();
return http.build();
}
}
In our branch for 3.0.0-RC2, the code is now as follows:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests((requests) -> requests
.requestMatchers(OPTIONS).permitAll() // allow CORS option calls for Swagger UI
.requestMatchers("/openapi/openapi.yml").permitAll()
.anyRequest().authenticated())
.httpBasic();
return http.build();
}
}
As you can see, the only difference is that I call requestMatchers instead of antMatchers. This method seems to have been renamed. The method antMatchers is no longer available. The end effect is still the same though. On our branch for 3.0.0-RC2, Spring Boot asks for basic authentication for the OpenAPI URL. Still works fine on 2.7.5.
Author: https://github.com/wilkinsona
#Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers(new AntPathRequestMatcher("/openapi/openapi.yml")).permitAll()
.anyRequest().authenticated())
.httpBasic();
return http.build();
}
Source: https://github.com/spring-projects/spring-boot/issues/33357#issuecomment-1327301183
I recommend you use Spring Boot 3.0.0 (GA) right now, not RC version.
Inside my WebSecurityConfig, I did this:
private static final String[] AUTH_WHITELIST = {
// -- Swagger UI v2
"/v2/api-docs",
"v2/api-docs",
"/swagger-resources",
"swagger-resources",
"/swagger-resources/**",
"swagger-resources/**",
"/configuration/ui",
"configuration/ui",
"/configuration/security",
"configuration/security",
"/swagger-ui.html",
"swagger-ui.html",
"webjars/**",
// -- Swagger UI v3
"/v3/api-docs/**",
"v3/api-docs/**",
"/swagger-ui/**",
"swagger-ui/**",
// CSA Controllers
"/csa/api/token",
// Actuators
"/actuator/**",
"/health/**"
};
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests( auth -> auth
.requestMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(withDefaults())
.addFilterBefore(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
//.addFilterAfter(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
#Bean
public SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeHttpRequests((requests) -> requests
.requestMatchers( new AntPathRequestMatcher("swagger-ui/**")).permitAll()
.requestMatchers( new AntPathRequestMatcher("/swagger-ui/**")).permitAll()
.requestMatchers( new AntPathRequestMatcher("v3/api-docs/**")).permitAll()
.requestMatchers( new AntPathRequestMatcher("/v3/api-docs/**")).permitAll()
.anyRequest().authenticated())
.httpBasic();
return httpSecurity.build();
}
This and using Dockerfile (doing mvn clean package and running .jar from Docker) made me had no issues with authentication inside swagger ui.
Hope this can help you :)
Use
http.securityMatcher("<patterns>")...
to specify authentication for endpoints.
authorizeHttpRequests((requests) -> requests
.requestMatchers("<pattern>")
only works for authorization, if you don't set securityMatcher , SecurityFilterChain by default gets any request for authentication. And any request will be authenticated by an authentication provider.
In your case, you can define two security filter, chains: one for public endpoitns, another for secured. And give them proper order:
#Bean
#Order(1)
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.securityMatcher(OPTIONS,"/openapi/openapi.yml").csrf().disable()
.authorizeHttpRequests((requests) -> requests
.anyRequest().permitAll() // allow CORS option calls for Swagger UI
);
return http.build();
}
#Bean
Order(2)
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.securityMatcher("/**")
.csrf().disable()
.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated())
.httpBasic();
return http.build();
}
The official documentation suggests an example which I have abridged here with your config:
http
.authorizeExchange((exchanges) ->
exchanges
.pathMatchers("/openapi/openapi.yml").permitAll()
.anyExchange().authenticated())
.httpBasic();
return http.build();
You could try this, since it changes the "request" for the "exchange" wording, in line with the migration to declarative clients (#PostExchange vs. #PostMapping) I suppose. Hope it helps.
My security cfg looks like:
Spring 3.0.0
#Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(requests -> requests
.requestMatchers(HttpMethod.GET, "/", "/static/**", "/index.html", "/api/users/me").permitAll()
.requestMatchers(HttpMethod.POST, "/api/users").permitAll()
.requestMatchers(HttpMethod.GET, "/api/users/login", "/api/users/{username}", "/api/users/logout", "/api/costumers", "/api/storages").authenticated()
.requestMatchers(HttpMethod.POST, "/api/costumers", "/api/storages").authenticated()
.requestMatchers(HttpMethod.PUT, "/api/costumers/{id}", "/api/storages/{id}").authenticated()
.requestMatchers(HttpMethod.DELETE, "/api/users/{id}", "/api/storages/{id}", "/api/costumers/{id}").authenticated()
.anyRequest().denyAll())
.httpBasic();
return http.build();
}
it works
This seems to be a bug in Spring Boot 3. I've raised an issue.

why is the AuthenticationManagerResolver called on a permitAll() (open endpoint)

Our spring boot 2.5.12 app is secured w/ a security configuration like this:
protected void configure(HttpSecurity http) throws Exception {
http
.cors().configurationSource(corsConfigurationSource)
.and()
.csrf()
.ignoringAntMatchers("/")
.and()
.authorizeRequests(authorizeRequests -> authorizeRequests
.mvcMatchers(GET, "/endpoint").hasAuthority("SCOPE_" + (Scope.READ))
.mvcMatchers(GET, "/endpoint/{reference}").permitAll()
.mvcMatchers(GET, "/error").permitAll()
.mvcMatchers(GET, "/info").permitAll()
.mvcMatchers(GET, "/health").permitAll()
.anyRequest().denyAll())
.oauth2ResourceServer()
.authenticationManagerResolver(authenticationManager())
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPoint);
}
w/ an AuthenticationManagerResolverBean:
public AuthenticationManagerResolver<HttpServletRequest> authenticationManager() {
return request -> {
...
...
...
};
}
it looks as if there's a bug as when i access the endpoint: /endpoint/ref123 it calls the AuthenticationManagerResolver even though this endpoint is open with a .permitAll(). So in the case the user accidentally provides an invalid token on this .permitAll() endpoint they aren't rejected.
if an endpoint is a .permitAll() then shouldn't spring not try to validate the token?
I didn't quite find why this is the behavior but we did find a workaround of sorts.
public void configure(WebSecurity web) {
web
.ignoring()
.mvcMatchers(GET, "/endpoint/{reference}");
}
It gets spring security to ignore tokens all together... valid or otherwise (which is what i thought permitAll did).

Basic auth HTTPbasic not working (unsecure) with ssl (HTTPS) spring boot ; if i remove httpBasic from configure it validates certificate (secure)

#Security config method
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/whitelist/**").permitAll()
.anyRequest().authenticated();
http.csrf().disable();
}
#application properties config server.ssl.enabled=true server.ssl.key-store=keystore.p12 server.ssl.key-store-password=password server.ssl.keyStoreType=PKCS12

Spring webflux security - Form based auth testing using postman

I am using Spring boot for backend and reactjs for front and both are running as different application with context switching on the load balancer so as to have the same app URL for both.
Context Switching:-
All requests routed to reach JS by default
Request starts with /api/v1/* will be routed to Spring boot backend.
Using Spring webflux security for the Spring boot application.
#Bean
public SecurityWebFilterChain securitygWebFilterChain(final ServerHttpSecurity http) {
http.securityContextRepository(securityContextRepository);
return http.authorizeExchange()
.matchers(PathRequest.toStaticResources().atCommonLocations())
.permitAll()
.pathMatchers(props.getSecurity().getIgnorePatterns())
.permitAll()
.anyExchange()
.authenticated()
.and()
.formLogin()
.and()
.exceptionHandling()
.authenticationEntryPoint((exchange, exception) -> Mono.error(exception))
.accessDeniedHandler((exchange, exception) -> Mono.error(exception))
.and()
.build();
}
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
public ReactiveAuthenticationManager authenticationManager() {
UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(
userDetailsService);
authenticationManager.setPasswordEncoder(passwordEncoder());
return authenticationManager;
}
#Bean
public ServerSecurityContextRepository securityContextRepository() {
WebSessionServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();
securityContextRepository.setSpringSecurityContextAttrName("app-security-context");
return securityContextRepository;
}
Using form based authentication here. All API's are restricted by default.
Need to test the login and API's with proper authentication. My react app is not yet ready so trying to use Postman for testing.
How to test the form login with postman?
Also, How to pass the login session for testing the other authenticated API's?

Resources