Open sckoh opened 9 years ago
I'm seeing this issue as well... any resolution?
We had a similar issue yesterday and figured out that we had serialized the authentication object with an old version of spring security (3.2.8) when the tokens were stored (access and refresh were effected), and then attempted to deserialize it with a new version (4.0.2). Authorization codes were also effected.
Spring Security has a SpringSecurityCoreVersion.SERIAL_VERSION_UID that is bumped periodically, and a major version got us a conflict that we weren't expecting.
We ended up having to read the serialized value out of the database, and then "bump" the serializationUID from the old number (320) to the new number (400). And by bump, we're talking re-writing bytes.
We added a test to our project to make sure that we are fully aware when these values change, as well as a couple other serial version uids that are in spring security oauth.
@dsyer any thoughts? Expected behavior? Alternative options? Any chance that a migration can be provided?
I guess if someone contributes some code we can consider it. For my part it's not a high priority because all tokens are essentially ephemeral so it shouldn't cost users much to re-acquire them if the token store is simply wiped.
If a token was obtain with a resource owner or client credentials grant, I agree with you; for three-legged oauth with tokens that were obtained using an authorization code, then you'd need to put all of your users back through the OAuth login flow. Until this was completed, you'd have a disruption of service.
Not sure I agree with that assessment. Social login providers put me through that process all the time, and I'm quite used to it, notwithstanding that they never tell me why they do it. However, I'm not sure that's relevant. You can always engineer a solution if you want to grant tokens based on previous user input. Maybe the ApprovalStore
would be a useful avenue of research, or indeed a custom TokenStore
if you care deeply about it?
Thanks @dsyer. We discussed this today and are going to get by with just overriding the serialization strategy for tokens and auth codes. Our tokens are too important to run the risk of an outage that requires the user to log back in.
I opened #617 so that we can override the serialization mechanism for authorization codes just like the JdbcTokenStore works. That would save us some code duplication.
I think the workaround solution is to use same spring-security-core version in your maven pom file to make sure different server can share the same SERIAL_VERSION_UID.
One workaround what I have done is,
1) Truncate table oauth_access_token
2) Make the new Oauth token request
It solved the problem for me. But this is an temporary fix.
Thanks @sornalingam. It works for me.
@benkiefer :+1: We are also getting the exactly same issue. @dsyer : We have around 10 million users spread across 1000 merchants. And making them login again will be a huge disruption of service.
Is there any solution or workaround to solve the issue elegantly?
@dhairyashil120 We had the exact same needs. Forcing the user to log in again was not an option.
It turns out that Spring "bumps" their serialization id and keeps it in sync with major/minor releases, and in our case, the underlying object had not actually changed (certainly not something we can count on in the future).
One of our devs wrote a groovy script that reads field out of the database and re-writes the authentication object's serialized id to the new value, and that got us through the crisis. We ended up having to rewrite the authentication object on both the refresh_token and access_token tables if I remember correctly.
We also explored storing the field as JSON using the hooks in the jdbc token store, but ultimately didn't have enough time to finish it. Note: Because of the way the class is structured, it isn't as simple as just using ObjectMapper to read and write the value. This also has drawbacks, because at least the serialization id is a clear warning that something changed and you need to pay attention.
We have since "pinned" our Spring security and oauth versions by writing tests that ensure that the serialization version doesn't change. If they fail, we know trouble is near.
This problem will become something we have to address before we can do a major update Spring again. Hope that info helps.
@benkiefer Were your issue sorted out after running groovy script for changing object's serialized id to new value? Or have done anything else for it?
Things worked fine after upgrading the serialized version uid for us.
The important thing is that you need to make sure all your code is is using the same seralization version, and that all your tokens are up to date with that version id. You can't live in a world of half done or some of your customers won't be able to use your product.
Keep in mind, this is a time bomb. We update spring security with much more care now.
@benkiefer : Thanks man..! We are going to use serialization uid "bump" approach. Also, We are not getting OAuth2Request object while deserialization. Had u also encountered such scenario ever?
It's been a long time since we worked through that issue, and I honestly can't remember.
As far as the deserialization fix goes, I can't stress how important it is to try this in a lower environment than production. It's actually fairly easy to produce the problem locally. Start up an oauth server on your current version of spring and get an oauth access token and refresh token for a user. Without cleaning your database, upgrade to the "next" version of spring. Then try and use that access token or refresh token. You should see the exception right away.
I'm glad to hear that not everyone is considering refresh tokens ephemeral. Forcing a user to log in again can't be the fix for everything.
Unless I'm misunderstanding the oauth token refresh/renewal mechanism, it seems to me too that the user is forced to login again after upgrading to a more recent release where the serialVersionUUID has been changed. For us it felt like a major issue.
In my case it seems only SimpleGrantedAuthority was affected by the changed UUID so I extended the JdbcTokenStore to handle the case where serialization fails:
private class FixedSerialVersionUUIDJdbcTokenStore extends JdbcTokenStore {
public FixedSerialVersionUUIDJdbcTokenStore(DataSource dataSource) {
super(dataSource);
}
@Override
protected OAuth2Authentication deserializeAuthentication(byte[] authentication) {
return deserialize(authentication);
}
@Override
protected OAuth2AccessToken deserializeAccessToken(byte[] token) {
return deserialize(token);
}
@Override
protected OAuth2RefreshToken deserializeRefreshToken(byte[] token) {
return deserialize(token);
}
@SuppressWarnings("unchecked")
private <T> T deserialize(byte[] authentication) {
try {
return (T) super.deserializeAuthentication(authentication);
} catch (Exception e) {
try (ObjectInputStream input = new FixSerialVersionUUID(authentication)) {
return (T) input.readObject();
} catch (IOException | ClassNotFoundException e1) {
throw new IllegalArgumentException(e1);
}
}
}
}
private class FixSerialVersionUUID extends ObjectInputStream {
public FixSerialVersionUUID(byte[] bytes) throws IOException {
super(new ByteArrayInputStream(bytes));
}
@Override
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();
if (resultClassDescriptor.getName().equals(SimpleGrantedAuthority.class.getName())) {
ObjectStreamClass mostRecentSerialVersionUUID = ObjectStreamClass.lookup(SimpleGrantedAuthority.class);
return mostRecentSerialVersionUUID;
}
return resultClassDescriptor;
}
}
Be aware that if SimpleGrantedAuthority changes, bad things might happen, hopefully it won't (https://github.com/spring-projects/spring-security/commits/master/core/src/main/java/org/springframework/security/core/authority/SimpleGrantedAuthority.java)
With this approach, serialized tokens with old serialVersionUUID are migrated to the new serialVersionUUID when they are used.
FYI - more info on how we got rid of using java serialization.
https://github.com/spring-projects/spring-security-oauth/issues/815
Still an issue. Any solution?
Server A is unable to deserialize OAuth2Authentication that is serialized by Server B
I'm using version 2.0.3