Description
Describe the bug
Using spring-boot-starter-oauth2-client
with spring-session-data-redis
and JSON serialization, when attempting an invalid authentication (for example by using the browsers back button) an OAuth2AuthenticationException
ends up being serialized into the Redis store, which then cannot be deserialized due to the security config.
To Reproduce
I noticed this problem while developing an open source project of mine. The steps to reproduce this are fairly complicated and require amongst other things a Discord account and registering a Discord bot application. Unfortunately I have no idea how to reproduce it more minimally, without talking to a third-party OAuth2 provider. At least the steps below allow to reproduce it using a port forward service, no full deployment necessary.
Maybe the stack traces and serialized data which are posted further down this post are enough to identify the problem without having to go through these reproduction steps.
Here we go:
The current state of the project as of writing this is this commit: https://github.com/wolfiabot/Wolfia/tree/de5fb5094d8c586d2d793c78adf0cbce08d52d60
A more-or-less detailed guide to start the backend and the frontend it locally is here: https://github.com/wolfiabot/Wolfia/tree/de5fb5094d8c586d2d793c78adf0cbce08d52d60/dashboard
Once everything is running, open it in your browser.
- Click on the login button in the top right
- Login to Discord if necessary, and click Authorize
- You will be redirected back to the application, which now detects you as logged in. All good so far!
- Click the back button in the browser to go back to Discord's OAuth2 screen
- Click Authorize again
The application throws an HTTP 500 for any requests to the backend now. Only clearing the cookies will help, as a session with the undeserializable data has been written into redis.
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: The class with org.springframework.security.oauth2.core.OAuth2AuthenticationException and name of org.springframework.security.oauth2.core.OAuth2AuthenticationException is not whitelisted. 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 https://github.com/spring-projects/spring-security/issues/4370 for details; nested exception is java.lang.IllegalArgumentException: The class with org.springframework.security.oauth2.core.OAuth2AuthenticationException and name of org.springframework.security.oauth2.core.OAuth2AuthenticationException is not whitelisted. 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 https://github.com/spring-projects/spring-security/issues/4370 for details
The full stack trace of the exception is here:
https://wastebin.party/ojidoxomol.pl
I used Redsmin to look into the exact content written into redis:
It appears to me the offending data is in the sessionAttr:SPRING_SECURITY_LAST_EXCEPTION
field:
https://wastebin.party/eseyinomeg.json
Expected behavior
Data that the framework serializes should also be deserializable by itself.
I can imagine two ways to achieve that: Either don't serialize any OAuth2AuthenticationException
s in the first place, or whitelist them.
Also not ruling out that something with my own setup is wrong here in which case some pointers are very welcome.
Sample
See the reproduction steps above.
Please let me know if anything is unclear and I'll do my best to provide further information.
I have suggested two possible approaches to a fix under "Expected behaviour". I'd be happy to work on a pull request to fix this problem if someone with better understanding of the internals of the framework can give me some directions which of those (or something else entirely) would be the correct approach.