Okta SP initiated Single Logout(SLO) configuration with Spring Security WebSecurityConfigurerAdapter - spring-security

I am using SAML authentication for my Spring boot application with Okta as IdP. The Single Signon(SSO) configuration is done and working as expected. But I am stuck with configuring Single Logout(SLO). I want to use SP initiated Logout. Please assist me to proceed further.
Mentioned below is the configure method that I created by extending WebSecurityConfigurerAdapter.
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/saml*").permitAll()
.anyRequest().authenticated()
.and()
.apply(saml())
.serviceProvider()
.keyStore()
.storeFilePath(this.keyStoreFilePath)
.password(this.password)
.keyname(this.keyAlias)
.keyPassword(this.password)
.and()
.protocol("https")
.hostname(String.format("%s:%s", "localhost", this.port))
.basePath("/")
.and()
.identityProvider()
.metadataFilePath(this.metadataUrl);
}

Related

Getting unauthorized after login in spring boot reactive

I'm trying to create form login with spring boot webflux. I can login and after login I'm redirectored successfully. But when I browse to a page that requires authentication, I'm getting error. If I remove the page from security config and get principal from ReactiveSecurityContextHolder I'm getting the user details.
Here is my security config:
public class SecurityConfig {
#Autowired
private UserService userService;
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/user/account")
.authenticated()
.anyExchange().permitAll()
.and()
.formLogin()
.loginPage("/user/login")
.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/"))
.authenticationManager(reactiveAuthenticationManager())
.and()
.logout()
.and()
.build();
}
#Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
return authentication -> userService.loginUser(authentication)
.switchIfEmpty(Mono.error(new UsernameNotFoundException(authentication.getName())))
.map(user -> new UsernamePasswordAuthenticationToken(user, null));
}
}
Do I need to do anything else in the ReactiveAuthenticationManager? Is that even required?
In this repository : https://github.com/mohamedanouarbencheikh/dashboard-auth-microservice
you have a complete example of spring security implementation with jwt in microservice architecture using spring cloud routing (gateway) which is based on reactive programming and Netty as application server, and angular as frontend
Answering to my own question so that anyone facing same problem can get some help:
The issue was resolved when I've changed the UsernamePasswordAuthenticationToken constructor and passed the authority parameter as null. This is really ridiculous. Here is the updated code:
#Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
return authentication -> userService.loginUser(authentication)
.switchIfEmpty(Mono.error(new UsernameNotFoundException(authentication.getName())))
.map(user -> new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()));
}
I've also simplified the config by removing authenticationSuccessHandler and authenticationManager from the config. Spring automatically redirects to /. For authenticationManager it automatically checks for a ReactiveAuthenticationManager bean and uses if found. Here is my updated config:
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.authorizeExchange()
.pathMatchers("/user/account")
.authenticated()
.anyExchange().permitAll()
.and()
.formLogin()
.loginPage("/user/login")
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessHandler(logoutSuccessHandler("/user/bye"))
.and()
.build();
}

Oauth2Login for only specific urls

I'm trying to have an oauth2 configuration setup for login through Spring Security. But only for specific urls.
My security config looks as follows.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/secured/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.clientRegistrationRepository(clientRegistrationRepository())
.authorizedClientService(authorizedClientService());
}
Basically I only want the oauth2Login to be trigger for urls that start with /secured. It seems to almost work, the only issue is whenever Spring trys to re-direct my session to google for authentication it triggers a 404.
The standard redirect for an oauth authentication should fire off to http://localhost:8080/oauth2/authorization/google, which my application attempts to do, but it 404's.
I'm assuming the http://localhost:8080/oauth2/authorization/google url is being blocked by some type of security config? But I cannot for the life of me figure out why.
I'm guessing I need to find the right combination of .permitAll() for any request going to "/oauth2/authorization/**"
I've tried this below.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/secured/**")
.authorizeRequests()
.antMatchers("/oauth2/authorization/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.clientRegistrationRepository(clientRegistrationRepository())
.authorizedClientService(authorizedClientService());
}
But that won't work.....does anyone see my issue? I have no other security config conflicting with this one, I'm at a bit of a loss.
According to previous answer, the version with no lambda-style should look like this right?
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests
.antMatchers("/secured/**").authenticated()
.anyRequest().authenticated()
.and()
.oauth2Login()
.clientRegistrationRepository(clientRegistrationRepository())
.authorizedClientService(authorizedClientService());
}
If we want oauth2Login only for "/secured/**", why have we to use authenticated() for antMatcher("/secured/**") and for anyRequest() (and not permitAll() for anyRequest())?
Thank you.
The first step to understanding the issue here is understanding the difference between http.antMatcher() and http.authorizeRequests().
Let's look at the following configuration.
(I am using the lambda-style configuration, available as of Spring Security 5.2, to make it more readable)
#Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/secured/**")
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2Login(oauth2Login -> oauth2Login
.clientRegistrationRepository(clientRegistrationRepository())
.authorizedClientService(authorizedClientService())
);
}
Here, we are specifying that HTTP security will only be invoked when matching "/secured/**".
In other words, the request will only be processed by the SecurityFilterChain if it matches "/secured/**".
This is a problem because the SecurityFilterChain is what initiates the Authorization Request from "/oauth2/authorization/google".
However, the SecurityFilterChain is not called because "/oauth2/authorization/google" does not match "/secured/**".
Consider the following configuration instead.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
)
.oauth2Login(oauth2Login -> oauth2Login
.clientRegistrationRepository(clientRegistrationRepository())
.authorizedClientService(authorizedClientService())
);
}
Here, the HTTP security will be invoked for all requests.
However, only requests that match "/secured/**" will require the user to authenticate.
The answer of #eleftheria-stein-kousathana brought me to another possible solution.
As stated it is important to know that the OAuth Endpoints won't called because the security config for them is bound to the antMatcher() call directly following the HttpSecurity object.
But as stated in the docs you can also change the baseUris of the redirectionEndpoint and the authorizationEndpoint
#Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/secured/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
//New Code Starting here
.authorizationEndpoint()
.baseUri("/secured/oauth2/authorization")
.and()
.redirectionEndpoint()
.baseUri("/secured/oauth2/code/*")
.and()
//new code ending here
.clientRegistrationRepository(clientRegistrationRepository())
.authorizedClientService(authorizedClientService());
}
Be sure to change the redirectUriTemplate of your clientRegistration and also the redirect uri configured at your AuthorizationServer
AND be sure to add the asterisk at the end of the redirectionEndpoint.baseUri ... that cost me some minutes to figure out what was wrong :-)

Spring Oauth2 with WebSecurity

I'am trying to setup Spring OAuth2 and using a custom WebSecurityConfigurerAdapter (#EnableWebSecurity).
As a base I copied the following two projects:
vanilla-sample
a client application with #EnableOAuth2Sso and the corresponding properties
This works as expected out-of-the-box.
But, when I try to add a WebSecurityConfigurerAdapter with #EnableWebSecurity to the Auto-Server (vanilla), it fails.
I'm getting a
Authentication Failed: Could not obtain access token when redirect back after login and authorization at the login page of the client.
I have setup a security.oauth2.resource.userInfoUri which worked just fine without the WebSecurityConfigurerAdapter.
Any ideas how to configure oauth2 with a WebSecurityConfigurerAdapter?
Change your http security configuration to something like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/", "/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests()
.anyRequest().authenticated();
}

Spring Boot, Spring Security and Thymeleaf: Apply CsrfFilter to website with form

I'm using Spring Security with Thymeleaf and want to create a login and a register form on different sites that make both use of CSRF protection. Protecting the login site is easy, as with the folloing WebSecurity configuration
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
Spring supports adding CSRF protection in general by the Security Filter Chain that is build in the configure method. This Filter Chain contains a CSRFFilter that adds/evaluates the CSRF token. This Filter Chain is then used for all matches defined in the above configuration. The mechanism of getting the Filters that are applied to a request can be found here in the method
doFilterInternal(ServletRequest, ServletResponse, FilterChain)
The problem is, if I add the "/register" site to this configuration, the user is redirected to the "/login" site first. If I don't add it to the above config, the mentioned FilterChain is not applied (and so not the CsrfFilter).
So what I want is to reuse the CsrfFilter in the Filter Chain of the "/register" site, but I don't know how to do that.
I'd prefer this approach to other ideas like writing a custom CSRF filter as suggested here or here.
From all of this i understood the problem is that you want people to access /register without needing to login first. Which is a simple fix:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.requestMatchers()
// add this line
.antMatchers("/register").permitAll().and
//
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
Turned out that the Spring Security Filter chain is applied to all endpoints mentioned in the list provided to requestMatchers().antMatchers().
So to use CSRF protection for a site that is not the login site, I just had to add it to this list and then permit all access to it, so there is no redirect to the login page. My final config looks like this
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.requestMatchers()
// consider these patterns for this config and the Security Filter Chain
.antMatchers("/login", "/register", "/oauth/authorize", "/oauth/confirm_access", "/oauth/token_key",
"/oauth/check_token", "/oauth/error")
.and()
// define authorization behaviour
.authorizeRequests()
// /register is allowed by anyone
.antMatchers("/register").permitAll()
// /oauth/authorize needs authentication; enables redirect to /login
.antMatchers("/oauth/authorize").authenticated()
// any other request in the patterns above are forbidden
.anyRequest().denyAll()
.and()
.formLogin()
// we have a custom login page at /login
// that is permitted to everyone
.loginPage("/login").permitAll();
}

Disable Basic Authentication while using Spring Security Java configuration

I am trying to secure a web application using Spring Security java configuration.
This is how the configuration looks:-
#Configuration
#EnableWebMvcSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private String googleClientSecret;
#Autowired
private CustomUserService customUserService;
/*
* (non-Javadoc)
*
* #see org.springframework.security.config.annotation.web.configuration.
* WebSecurityConfigurerAdapter
* #configure(org.springframework.security.config
* .annotation.web.builders.HttpSecurity)
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/","/static/**", "/resources/**","/resources/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic().disable()
.requiresChannel().anyRequest().requiresSecure();
// #formatter:on
super.configure(http);
}
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
// #formatter:off
auth
.eraseCredentials(true)
.userDetailsService(customUserService);
// #formatter:on
super.configure(auth);
}
}
Notice that I have explicitly disabled HTTP Basic authentication using:-
.httpBasic().disable()
I am still getting HTTP Authenticaton prompt box while accessing a secured url. Why?
Please help me fix this.
I just want to render the default login form that comes bundled.
Spring Boot Starter Version : 1.1.5
Spring Security Version : 3.2.5
Thanks
First of all, calling super.configure(http); will override whole your configuration you have before that.
Try this instead:
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic().disable();
In case you use Spring Boot, the documentation states:
To switch off the Boot default configuration completely in a web
application you can add a bean with #EnableWebSecurity
So if you want to fully customize itself that might be an option.
Just to make it clear... You just need to put #EnableWebSecurity annotation on your main application class or application configuration class.
You can disable the formLogin through the HttpSecurity instance as follow:
http.authorizeRequests().antMatchers("/public/**").permitAll()
.antMatchers("/api/**").hasRole("USER")
.anyRequest().authenticated()
.and().formLogin().disable();
This will lead receiving 403 Http error when trying to access any secured resource
Anonymous option worked for me. My code like
http.csrf().disable().headers().frameOptions().sameOrigin().and().
authorizeRequests().anyRequest().anonymous().and().httpBasic().disable();
Suitable for Spring Boot or folks using OAuth
#Profile("test")
#EnableWebSecurity
static class BasicWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().anonymous().and().httpBasic().disable();
}
}
If you are using #EnableOAuth2Client or #EnableResourceServer, then in test profile switch to basic auth and then disable the same. In Spring Boot,to switch off the spring security default configuration completely in a web application you need to add a bean with #EnableWebSecurity
The following worked for me:
http
.authorizeRequests()
.anyRequest().permitAll();

Resources