spring-authorization-server: OAuth2ClientAuthenticationToken should not be persisted across requests
Describe the bug Sometimes the server returns a 500 error with the message:
java.lang.IllegalArgumentException: The class with org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken and name of org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken is not in the allowlist. If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. If the serialization is only done by a trusted source, you can also enable default typing. See spring-projects/spring-security#4370 for details
To Reproduce
I encountered the bug while testing a resource server in conjunction with a new Spring Authorization Server’s authorization code flow. The API tool I use automatically does the OAuth2 authentication, using the same session cookie from last time when it gets a new token, and that then stops working due to the 500 error. This is a contrived example, but it does trigger the bug reliably:
-
Modify the sample authorization server, adding
.redirectUri("https://oidcdebugger.com/debug")
to theRegisteredClient
and then run the server. -
Open a browser, go to https://oidcdebugger.com/debug, open the network tab of the browser inspector.
-
Fill the form in, log-in, and get an authorization code.
-
Look in the browser inspector to get the JSESSIONID cookie from
/oauth2/authorize
. -
I’m not sure how quickly the next steps need to be done, but now POST directly to the
/oauth2/token
endpoint. Run curl or your preferred API client:
export AUTHCODE=“YOUR CODE HERE” && curl -X “POST” “http://127.0.0.1:9000/oauth2/token” \ -H ‘Cookie: JSESSIONID=COOKIE FROM BROWSER’ \ -H ‘Content-Type: application/x-www-form-urlencoded; charset=utf-8’ \ -u ‘messaging-client:secret’ \ –data-urlencode “grant_type=authorization_code” \ –data-urlencode “code=$AUTHCODE” \ –data-urlencode “redirect_uri=https://oidcdebugger.com/debug”
-
A token should be correctly returned.
-
Click “Start Over” in the browser, which will hopefully cause the browser to send the same JSESSIONID with the new call to
/oauth2/authorize
, and get the new authorization code. Try the POST again with the new code and the same JSESSIONID. It should return a 500 error, and in the logs will be the exception.
Expected behavior
I expected to get a new token, and if not, an error message rather than an exception.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 2
- Comments: 24 (6 by maintainers)
Commits related to this issue
- Remove temporary HttpSessionSecurityContextRepository Issue gh-482 — committed to spring-projects/spring-authorization-server by jgrandja 2 years ago
- Fix OAuth2AuthorizationCodeGrantTests Issue gh-482 — committed to jgrandja/spring-authorization-server by jgrandja 2 years ago
- Fix OAuth2AuthorizationCodeGrantTests Issue gh-482 — committed to spring-projects/spring-authorization-server by jgrandja 2 years ago
- HttpSessionSecurityContextRepository does not persist @Transient Authentication Related https://github.com/spring-projects/spring-security/pull/9993 Closes gh-482 — committed to doba16/spring-authorization-server by jgrandja 3 years ago
- Remove temporary HttpSessionSecurityContextRepository Issue gh-482 — committed to doba16/spring-authorization-server by jgrandja 2 years ago
- Fix OAuth2AuthorizationCodeGrantTests Issue gh-482 — committed to doba16/spring-authorization-server by jgrandja 2 years ago
@jgrandja @colin-riddell
I believe what @colin-riddell is referring to (since we discussed this together offline) is a custom scenario using a different authentication mechanism other than form login, for example using JWT authentication. In this case, it seems possible that
JwtAuthenticationToken
would be placed in thejava.security.Principal
attribute as it is actually the principal representing the user’s authentication.Our mixin inherits from Spring Security the ability to persist a
UsernamePasswordAuthenticationToken
into attributes. But in case of a different authentication, there would still be the need for an application to provide their own mixins to ensure theOAuth2Authorization
can be persisted via JSON in the database. I feel this would be another example where a How-to guide could capture how this can be customized, and potentially such a mixin (that Colin has already developed a draft of) could be part of that guide.Hope that helps.
Great, thank you @jgrandja and @sjohnr! The temporary fix works perfectly.
add Annotation com.fasterxml.jackson.databind.annotation.JsonSerialize
Hi @colin-riddell. Since that is not something that’s happening out of the box (e.g. in the existing sample), probably not at this time. However, it may be required in the future. As a current example, when using the UserInfo endpoint you need to enable
oauth2ResourceServer().jwt()
. So it’s very close to a core requirement, but not enabled out-of-the-box as of yet.Awesome. Thanks for the heads up! I will review it as soon as I can and get back to you with that feedback.
@sjohnr I just encoutered the issue when deserializing
OAuth2ClientAuthenticationToken
. I am implemententing a custom grant type (the password grant type) but when I try to refresh the token I get this error:The issue comes from OAuth2RefreshTokenAuthenticationProvider where we have to provide authorization’s attribute. To provide one I registered it like this:
The serialization seems to work but not the deserialization. You mentionned a How-to guide in your comment, where can I find a reference guide or should I define a custom mixin? Thank you
@mbarringer @edwardzjl @colin-riddell This issue is related to a bug in Spring Security. See spring-security#9993. There is a fix in place and will be released in 5.7.0 GA, which is a way out.
FYI, the
OAuth2ClientAuthenticationToken
andJwtAuthenticationToken
are marked as@Transient
and therefore should NOT be persisted across requests. However, it is being persisted across requests, resulting in this bug.Since Spring Security 5.7.0 GA is a way out, I’ve applied a temporary fix via d0e1107
It seems that the
attributes
column is not properly (serialized and) deserialized. I’m interested in making a PR of this issue.