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

401 and 403 HTTP statuses never return response body #32212

Closed
wujek-srujek opened this issue Sep 1, 2022 · 6 comments
Closed

401 and 403 HTTP statuses never return response body #32212

wujek-srujek opened this issue Sep 1, 2022 · 6 comments
Labels
status: invalid An issue that we don't feel is valid

Comments

@wujek-srujek
Copy link

(I found similar issues but in my case even adding authorize("/error", authentication) or even authorize("/error", permitAll) doesn't help. I have no explicit session policy configuration.)

When using Spring Boot 2.7.3 with Spring Security Resource Server JWT, any exception that is eventually mapped to 401 or 403, results with an empty response body (I'm using the default ErrorAttributes from Spring Boot). I remember it working differently some time ago, but cannot nail the version that changed this.

What I would like the behavior to be: when the user is authenticated I would like to send 401 or 403 with request bodies. Is this possible?

Please see the attached example, run it with ./mvnw spring-boot:run. (I'm using a fake JWT decoder so that the sample doesn't require real tokens, it doesn't influence the behavior.) When you call http://localhost:8080/test/401 (or 403) you will get the response without the body, but using any other status does return the body.

Switch to basic auth (remember to disable the tokenDecoder @Bean) and it works fine.

spring_forbidden_test.zip

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 1, 2022
@mbhave
Copy link
Contributor

mbhave commented Sep 6, 2022

Thanks for the sample. I'm not sure what the fake decoder is intended for but I don't think it's doing what it's supposed to do. If I hit http://localhost:8080/test/401, I don't get to the controller at all as Spring Security denies access.

Once access is denied to the controller, the difference in behavior for jwt and basic auth is due to the way Spring security's BearerTokenAuthenticationEntryPoint and BasicAuthenticationEntryPoint behave.

BasicAuthenticationEntryPoint calls response.sendError() which results in an error dispatch. If access to the error page is allowed, the response will contain a body. If not, it will just contain the original status code.

BearerTokenAuthenticationEntryPoint does not call response.sendError(). This means there is no error dispatch and Spring Boot's error handling infrastructure is not invoked.

However, it looks like the issue you're reporting is after Spring Security has been able to successfully authenticate a user. Can you modify the sample so that it actually does that with jwt authentication?

@mbhave mbhave added the status: waiting-for-feedback We need additional information before we can continue label Sep 6, 2022
@wujek-srujek
Copy link
Author

wujek-srujek commented Sep 6, 2022

You are right, sorry, I messed the sample up somehow. Please try the new sample attached. The folder contains a Postman collection with which you can:

  • Get an access_token. When you invoke this, it will set the access_token collection variable.
  • Invoke http://localhost:8080/test/402. It will automatically use the access_token collection variable set in the previous step.

To reproduce:

  1. Start with ./mvnw spring-boot:run.
  2. Fetch a token using the Postman collection.
  3. Invoke the other request to http://localhost:8080/test/402. You will see the body comes back.
  4. Change the other request to invoke http://localhost:8080/test/401 or .../403. You will see that no body comes back.
  5. Edit src/main/kotlin/com/test/foo/Foo.kt and set /error to permitAll, and redo the calls in steps 3 and 4. You should see that it makes no difference.

This means that even though the user is authenticated 401 or 403 don't result in sending an error body.

spring_forbidden_test.zip

@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 Sep 6, 2022
@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided status: waiting-for-feedback We need additional information before we can continue labels Sep 7, 2022
@mbhave
Copy link
Contributor

mbhave commented Sep 9, 2022

I wasn't able to run your sample (I'm not too familiar with how postman collections work). But I think what is missing is the RequestAttributeSecurityContextRepository configuration. Even though you haven't configured session management to be stateless explicitly, a JwtAuthenticationToken is annotated with @Transient which means it won't be saved by the HttpSessionSecurityContextRepository. Using a RequestAttributeSecurityContextRepository ensures that the original authentication is available on an error dispatch thereby allowing access to Spring Boot's error controller.

You can add RequestAttributeSecurityContextRepository to your security configuration as shown here.

Please let us know if that fixes the problem for you. We have an open issue for documenting this #30761.

@mbhave mbhave added the status: waiting-for-feedback We need additional information before we can continue label Sep 9, 2022
@wujek-srujek
Copy link
Author

wujek-srujek commented Sep 10, 2022

@mbhave You don't need to use Postman to test this, I just thought it would be helpful, but you can use whatever you like. Here is an example with curl:

  1. Start the server using ./mvnw spring-boot:run
  2. Invoke:
curl --request POST 'https://dev-arg0-cet.us.auth0.com/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: did=s%3Av0%3A746b8e10-30d0-11ed-a1d5-8f38b5a150f7.ozVaQs3tQb6tEeTOaueXkG2bGcHZYXxNZtGEeDxyzvA; did_compat=s%3Av0%3A746b8e10-30d0-11ed-a1d5-8f38b5a150f7.ozVaQs3tQb6tEeTOaueXkG2bGcHZYXxNZtGEeDxyzvA' \
--data-urlencode 'client_id=KaGx9BUOBQ692FG1MLgRDtLUIcI2FMUs' \
--data-urlencode 'client_secret=HCpWio9nwoLopX0AnVFGksdTmULrH1COqEGCWMQA08qz1IedqkiASRBcjaRhUdln' \
--data-urlencode 'audience=https://test.spring/api' \
--data-urlencode 'grant_type=client_credentials'

to get the token.
3. You will get a JSON back, copy the value of the field access_token and invoke this:

curl --request GET 'http://localhost:8080/test/402' --header 'Authorization: Bearer <the access token value copied in previous step>'

You will see that the request to http://localhost:8080/test/402 returns the body in the response. Requests to http://localhost:8080/test/401 or http://localhost:8080/test/403 never return a body.

I will try your suggestion when I'm at my computer, thanks.

@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 Sep 10, 2022
@wujek-srujek
Copy link
Author

wujek-srujek commented Sep 10, 2022

Yes, that was it, the repository fixed the problem. Thank you for looking into this.

I remember reading about the repository in the docs before but I didn't understand how this would affect me and why I would need it, so I never configured it. Maybe some examples about why one would need it would be helpful?

@philwebb philwebb added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels Sep 10, 2022
@scottfrederick scottfrederick closed this as not planned Won't fix, can't repro, duplicate, stale Sep 10, 2022
@mbhave
Copy link
Contributor

mbhave commented Sep 12, 2022

Thanks for letting us know that it worked for you @wujek-srujek. We have an open issue to add documentation for this #30761.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

6 participants