Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto-configure RequestAttributeSecurityContextRepository for OAuth2 resource server #29655

Closed
datagitlies opened this issue Feb 5, 2022 · 16 comments
Labels
status: superseded An issue that has been superseded by another

Comments

@datagitlies
Copy link

This appears to have broken in 2.6.X as older versions (I tried 2.5.X) do not exhibit the behavior described below.

Step 1: provide a WebSecurityConfigurerAdapter and configure it with the STATELESS session creation policy:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().authorizeRequests()
                .antMatchers("/test-user").hasRole("USER")
                .antMatchers("/test-admin").hasRole("ADMIN")
                .antMatchers("/error").authenticated()
                .and().httpBasic();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("{noop}password").roles("USER").and()
                .withUser("admin").password("{noop}password").roles("ADMIN", "USER");
    }
}

Step 2: setup a simple controller

@Controller
public class ExampleController {

    @GetMapping(path = {"test-user", "test-admin"})
    public void test(HttpServletResponse response) {
        response.setStatus(HttpServletResponse.SC_OK);
    }
}

Step 3: run the application and send a request to a resource the user is not authorized to view

curl -v -u user:password http://localhost:8080/test-admin

Expected Behavior: the user receives a HTTP 403 Forbidden response status code and a response body (such as the below):

{
   "timestamp":"2022-02-04T23:42:41.621+00:00",
   "status":403,
   "error":"Forbidden",
   "path":"/test-admin"
}

Actual Behavior: the user receives a HTTP 403 Forbidden response status code with no response body

Note: if the STATELESS session creation policy is removed from the above configuration, it works as expected.

@snicoll
Copy link
Member

snicoll commented Feb 5, 2022

@datagitlies thanks for the report. By sample, we mean something we can run ourselves and not code in text we'd have to copy and paste with a chance of missing something that wasn't described. Can you move that in actual project please? You can do so by attaching a zip to this issue or sharing a link to a GitHub repository.

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label Feb 5, 2022
@datagitlies
Copy link
Author

Sure. The error-response-body.zip file contains a full sample you can run @snicoll

One more note. By changing the version of the spring-boot-starter-parent in the pom.xml file from 2.6.3 to 2.5.9 ... it works as expected again. Hence, I believe this is a problem introduced in 2.6.X

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Feb 5, 2022
@mbhave
Copy link
Contributor

mbhave commented Feb 7, 2022

Thanks for the sample @datagitlies. This commit was added to support previously authorized users to access the error page. The fix covers all scenarios except the case where the error page security rules are different from the original request (which is the case here). We discussed that case and didn't think it would be a situation that would arise very often and thought it was better to restrict access when we don't have the Authentication (in case of stateless session policy) than to allow it.

Can you describe in more detail why you want an unauthorized user to view the error details? In this example, it seems like a regular user shouldn't be allowed to view the error details while trying to access an admin page.

We could make it easier to exclude the error security filter for users who want to revert to 2.5.x behavior. However, that would allow unauthenticated access to the error page. You can see that on the sample app with 2.5.9, a request with no credentials can view the error details. The same behavior can be achieved on 2.6.3 with a permitAll() for the error page.
Flagging for team meeting to see if there's anything we can do here.

@mbhave mbhave added the for: team-meeting An issue we'd like to discuss as a team to make progress label Feb 7, 2022
@datagitlies
Copy link
Author

datagitlies commented Feb 8, 2022

Can you describe in more detail why you want an unauthorized user to view the error details?

I think of it like this. If you have authenticated yourself with valid credentials and you are a user of my application... I owe it to you to clearly communicate error information to you.

Also, in my sample there is only one /error and it is for all users and all scenarios. Hence my sample uses .antMatchers("/error").authenticated() because "authenticated" includes both the "USER" and "ADMIN" roles. Even if I were to change it to .antMatchers("/error").hasAnyRole("USER", "ADMIN") this issue would remain. Are you thinking it would be better to have an error page for ADMIN users that is defined separately and secured differently than the error page for regular USER users @mbhave ? If yes, how likely is it that the error logic would be any different between the two? I think it's probably pretty common for applications to have a single, shared /error which is usually just the default one provided by spring boot.

Regardless, I believe the real issue here is the lost security context for STATELESS session. It appears that this new error security filter is treating a single request as if it were two separate requests which seems faulty if you can't consistently access the Authentication from the "first" request and apply it appropriately to the "second" request. Anything less than that the framework has to make assumptions or take an opinionated view of how it should work. Also, it sets up the scenario where the functionality is inconsistent. Stateful or stateless, I think the behavior should be consistent across both.

@svilen-ivanov-kubit
Copy link

I'm also affected by this problem: the STATELESS requirement comes from the fact that I use JWT for authentication which makes this a common use case.

@wilkinsona
Copy link
Member

@svilen-ivanov-kubit, as @mbhave described above, you should be able to restore Boot 2.5's behaviour by using permitAll() when configuring access to /error. Can you please give that a try?

@datagitlies
Copy link
Author

@wilkinsona I understand what you and @mbhave provided with permitAll() is a workaround but has your thinking changed since #28953 (comment)

If a user is authenticated, they should received a body in an error response without permitting all to access the error page.

@wilkinsona
Copy link
Member

My thinking hasn't changed, but what I said there wasn't in the context of a stateless session creation policy.

It's unfortunate that Spring Security does not preserve a user's authentication across multiple dispatches for the same original request when it has been configured not to use a session. I do not fully understand why the authentication cannot be preserved even without a session when only a single request has been made by the client. We'll discuss this with the Security team.

Given Spring Security's current behaviour, we can either allow access by anyone to the error page (as Boot 2.5 did) or we can secure it but require a stateful session policy (as Boot 2.6 now does). If you're using 2.6 with a stateless session policy, you can opt back into Boot 2.5's behaviour by using permitAll(). I don't really consider this to be a work around, more an accurate reflection of how Spring Security currently manages the authentication and how servlet container error pages work.

@svilen-ivanov-kubit
Copy link

I tried the .permitAll() suggestion but without any luck. I see Jetty's default 401 HTML page instead of JSON response with 401 in 2.6.x:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 401 Unauthorized</title>
</head>
<body><h2>HTTP ERROR 401 Unauthorized</h2>
<table>
<tr><th>URI:</th><td>/....the path I request....</td></tr>
<tr><th>STATUS:</th><td>401</td></tr>
<tr><th>MESSAGE:</th><td>Unauthorized</td></tr>
<tr><th>SERVLET:</th><td>dispatcherServlet</td></tr>
</table>

</body>
</html>

I'll try to find out how my setup differs from the minimal reproducible project (except that I use JWT for authentication).

@mbhave
Copy link
Contributor

mbhave commented Feb 25, 2022

@svilen-ivanov-kubit A sample would be very helpful, thank you.

@mbhave
Copy link
Contributor

mbhave commented Feb 28, 2022

Blocked on spring-projects/spring-security#10918.

@mbhave mbhave added the status: blocked An issue that's blocked on an external project change label Feb 28, 2022
@svilen-ivanov-kubit
Copy link

@svilen-ivanov-kubit A sample would be very helpful, thank you.

I updated our WebSecurityConfigurerAdapter by removing some cruft and the issue is no longer reproducible. Thank you.

@philwebb philwebb added this to the 2.6.x milestone Mar 30, 2022
@philwebb philwebb added type: bug A general bug for: team-meeting An issue we'd like to discuss as a team to make progress and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided status: blocked An issue that's blocked on an external project change for: team-meeting An issue we'd like to discuss as a team to make progress labels Mar 30, 2022
@philwebb
Copy link
Member

philwebb commented Apr 20, 2022

For 2.6 we're going to recommend that .permitAll() is used. For 2.7 we're going to auto-configure RequestAttributeSecurityContextRepository for the OAuth2 resource server and also document that users can configure it themselves.

@philwebb philwebb modified the milestones: 2.6.x, 2.7.x Apr 20, 2022
@philwebb philwebb added type: enhancement A general enhancement and removed type: bug A general bug for: team-meeting An issue we'd like to discuss as a team to make progress labels Apr 20, 2022
@philwebb philwebb changed the title Error response body missing for authenticated users when utilizing the STATELESS session creation policy Auto-configure RequestAttributeSecurityContextRepository for OAuth2 resource server Apr 20, 2022
@datagitlies
Copy link
Author

@philwebb yeah, I saw this

Introduced SecurityContextHolderFilter - Ability to require explicit saving of the SecurityContext

Assume that means spring-boot 2.6.x will not be using spring-security 5.7.x then?

@mbhave
Copy link
Contributor

mbhave commented Apr 21, 2022

@datagitlies Spring Boot 2.6.x uses Spring security 5.6.x, so yes it won't be using spring security 5.7.x

@mbhave
Copy link
Contributor

mbhave commented Apr 21, 2022

We think that auto-configuring RequestAttributeSecurityContextRepository for the OAuth2 auto-configuration is not required because this issue happens only when there are multiple users with different permissions and access to the originally logged in user is required. I am going to close this issue in favor of the documentation issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: superseded An issue that has been superseded by another
Projects
None yet
Development

No branches or pull requests

7 participants