Spring Security OAuth2 configuring failureUrl makes the url inaccessible - spring-security

I have a spring security based boot application for which I have configured an endpoint names /test for which I return a test.html page
#RequestMapping("/test")
public String test() {
return "test.html";
}
The endpoint works fine by itself, however if I set it as an OAuth2 failureUrl it becomes unavailable...
http.csrf().disable()
.httpBasic().disable()
.formLogin().disable()
.authorizeRequests()
.antMatchers("/test").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.failureUrl("/test");
Is this expected behavior? When I do this, spring security properly redirects to /test on authentication failure but /test is inaccessible and it ends up showing a generated page.

The behaviour of failureUrl differs based on whether or not a custom loginPage is configured.
Since you have not customized loginPage, the framework will intercept the failure URL ("/test") and generate the default error page, which is simply the default login page with an error message.
That is why you see the generated login page with accessing "/test".
Your Controller mapping for "/test" is ignored.
To tell the framework not to generate the error page, you can configure the failureHandler instead.
http
.oauth2Login((oauth2Login) -> oauth2Login
.failureHandler(new SimpleUrlAuthenticationFailureHandler("/test"))
);
Note: This may be confusing because the Javadoc for failureUrl does not describe its behaviour properly. I have created a GitHub issue in the Spring Security backlog to fix this.

Related

Configure the landing page after SSO authentication

I want to know how to setup the relay state with the new saml library. Basically once I am authenticated via the asserting party, I want to have a relay state url (a JSP in my application), where I should land. In the saml extensions library, as far as I know, the relay state url was set in the SAMLMessageContext object.
Actual Behaviour The mechanism of setting the page where I would be redirected to after SAML login has changed. I am not sure how to set the desired JSP where I want to land in the new library.
Expected behavior After the successful call to the assertionConsumerServiceLocation in my application, I should be taken to a Url configured by me (Relaystate). I need help in configuring this URL.
I tried to set up the relay state like this :-
Saml2AuthenticationRequestResolver authenticationRequestResolver(
RelyingPartyRegistrationResolver registrations) {
OpenSaml4AuthenticationRequestResolver authenticationRequests =
new OpenSaml4AuthenticationRequestResolver(registrations);
authenticationRequests.setRelayStateResolver(relayStateResolver);
return authenticationRequests;
}
I have defined the relayStateResolver like the following :-
private Converter<HttpServletRequest, String> relayStateResolver = (request) -> "my_desired_jsp_url_string";
Are my above configurations correct, and would they help me in landing on the desired JSP page after successful login?
Currently, below is the error I am facing with the above implementation. I am working to fix that (I need to get the Opensaml4 from Shibboleth's repository of artifacts), but wanted to know if the above configuration is correct before making that fixing effort.
Jan 03, 2023 5:54:28 AM org.apache.catalina.core.StandardWrapperValve
invoke SEVERE: Servlet.service() for servlet [dispatcher] in context
with path [/company] threw exception [Filter execution threw an
exception] with root cause java.lang.NoSuchMethodError:
org.opensaml.saml.saml2.core.AuthnRequest.setIssueInstant(Ljava/time/Instant;)V
at
org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver.lambda$resolve$1(OpenSaml4AuthenticationRequestResolver.java:60)
at
org.springframework.security.saml2.provider.service.web.authentication.OpenSamlAuthenticationRequestResolver.resolve(OpenSamlAuthenticationRequestResolver.java:133)
at
org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver.resolve(OpenSaml4AuthenticationRequestResolver.java:59)
at
org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter.doFilterInternal(Saml2WebSsoAuthenticationRequestFilter.java:184)
at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
Because RelayState can be used for several things and is not always used to describe the post-login redirect URL (the spec says MAY), it is not defaulted to do this.
You can configure Spring Security to redirect to the RelayState parameter by configuring a SimpleUrlAuthenticationSuccessHandler like so:
#Bean
public SecurityFilterChain appEndpoints(HttpSecurity http) {
SimpleUrlAuthenticationSuccessHandler successHandler =
new SimpleUrlAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter(Saml2ParameterNames.RELAY_STATE);
http
.saml2Login((saml2) -> saml2
.authenticationSuccessHandler(successHandler)
// ...
)
// ...
return http.build();
}
You will also need to configure the relay state resolver, as you have already shown in your post.

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 MVC Test MockMvc - how do I configure a url mapping prefix

I have a DispatcherServlet that has a URL mapping /api1 and subsequentially a Controller with a mapping #GetMapping("/resource1") for a controller method. So basically I have a valid URL /api1/resource1 that should be handled by the mentioned controller.
Also, the setup incorporates a Spring Security Filter that matches requests /* as it secures other URLs not handled by Spring (but Jersey for example).
The API secured by the Spring Security Filter is setup like
protected void configure(HttpSecurity http) throws Exception {
//#formatter:off
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.requestMatchers()
.antMatchers("/api1/**")
.and()
.authorizeRequests()
.antMatchers("/**")
.authenticated()
For testing I use the MockMvc* support to setup a mocked web environment including a Spring security setup
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build()
I want to test that security checks are applied and the controller method is called on successful security checks.
when:
def result = mvc.perform(
get('/api1/resource1')
.header(HttpHeaders.AUTHORIZATION, "Bearer " + apiToken))
then:
result.andExpect(status().isOk())
The code above is based on the Spock framework with the MockMvc stuff.
All of the security checks are passing so the Spring security setup is complete, but finally the controller should be invoked but fails with a 404 status i.e the resource - this is the mapped controller method - is not found.
I'm confident that it fails because the mocked setup does not incorporate a the /api dispatcher servlet mapping. To proof that assumption I can modify the controller method mapping to #GetMapping("/api1/resource1") and the test will result in a HTTP OK (200).
So, my question is, is it possible to configure a kind of URL prefix in the MockMvc setup?
There is one constraint, the code base is not using Spring Boot (and can't for a while in future)!
Edit:
I added the following to my test to have all requests set the servletPath.
static MockHttpServletRequestBuilder get(String urlTemplate, Object... uriVars) {
new MockHttpServletRequestBuilder(HttpMethod.GET, urlTemplate, uriVars)
.servletPath('/api1')
}
I think you just need to configure the contextPath for the request.
See org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder.contextPath(String) for details.

How to configure /public, /protected, /private access paths in Spring Oauth2 Security

I have a very specific requirement in my project related to identity & authorization. I want to open 3 paths /public/, /protected/ & /private/ from my REST service module, which will behave as follows:
URLs starting with /public/ can be accessed without any authentication or authorization.
URLs starting with /private/ can be accessed only if the user is authenticated.
URLs starting with /protected/ can be accessed only if the user is authenticated as well as authorized.
To achieve this I have built a Configurator by extending "spring resource server configurator & overriding the configure method". But unfortunately it's not working. I have also tried to use "spring web service configurator & using the ignore ant url support " but the same is also not working. The configuration which is working only for /private/ & /protected/ URLs is as follows.
http.anonymous()
.disable()
.requestMatchers()
.antMatchers("/protected/**", "/private/**")
.and();
for (String protectedApiEp : configuredApis) {
http.authorizeRequests()
.antMatchers("/protected/" + protectedApiEp + "/**")
.hasAuthority(protectedApiEp);
}
http.authorizeRequests()
.antMatchers("/protected/**").denyAll()
.antMatchers("/private/**").permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
Can anyone guide me how I can enable /public/ URLs as open to all users, with the above configuration?
The following configuration should work:
#EnableWebSecurity
public class WebApplicationSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
// Allow Spring Security to authorize requests.
http
.authorizeRequests()
// Allow anyone to access URLs starting with /public/.
.antMatchers("/public/**").permitAll()
// Allow anyone with the protected role to access URLs starting with /protected/.
.antMatchers("/protected/**").hasAuthority("protected")
// Allow anyone who is authenticated successfully to access all other URLs.
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
Here is a sample application that shows this configuration in action. Start the application as mvn clean spring-boot:run and then navigate to http://localhost:8080 to access the application.

why Spring Security Exception Translation Filter creates 403 Response Code for default configuration

I am pretty new to Spring Security land. I am using programmatic configuration of Spring Security with servletApi() which is pretty neat.
Here is the configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.securityContext().and()
.servletApi().and()
.authorizeUrls()
.antMatchers("/login").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/**").authenticated();
}
I am using http servlet api login I am not using any filter for this.
In case a unauthorised request, ExceptionTranslationFilter uses Http403EntryForbiddenEntryPoint to return 403 forbidden status.
In my scenario:
If user does not authenticated, a 401 status code should return.
If user authenticated but not authorised, a 403 status code should return.
But default configuration creates 403 status for both case.
Here are my questions:
Why is the default entry point is Http403EntryForbiddenEntryPoint? It can be 401?
If I change Http403EntryForbiddenEntryPoint to Http401EntryForbiddenEntryPoint, does It create a problem?
Thanks

Resources