Spring-security-oauth: setting reuseRefreshToken to true not working.

Created on 6 Jul 2017  路  15Comments  路  Source: spring-projects/spring-security-oauth

Hi,
I am using JwtTokenStore and I am trying to reuse Refresh Token.

I set up the configuration to support reusing the refresh token in two places.
endpoints.reuseRefreshTokens(true); and
tokenService.setReuseRefreshToken(true);

I am observing that when refreshToken call is made, JwtAccessTokenConverter enhance method is called and is returning new refreshToken irrespective of the settings.

Code from JwtAccessTokenConverter enhance method where it is returning the new refresh token.
{code}
DefaultOAuth2RefreshToken token = new DefaultOAuth2RefreshToken(encode(
encodedRefreshToken, authentication));

{code}

In My understanding, reuseRefreshToken setting it to true should return the same refresh token.
Am I missing any thing? Is it known issue?

invalid

Most helpful comment

I created this Class extending the JWTAccesTokenConverter that in case the existing refresh token is already a valid JWT it replaced it with the existing one

public class PreserveRefreshTokenJwtAcesTokenConverter extends JwtAccessTokenConverter implements TokenEnhancer {

    private static final Logger LOG =LoggerFactory.getLogger(PreserveRefreshTokenJwtAcesTokenConverter.class);

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        OAuth2AccessToken converted = super.enhance(accessToken, authentication);
        if(refreshToken!=null&& refreshToken.getValue()!=null) {
            //Preserve previous refresh token if it was already a valid JWT token.
            try {
                JwtHelper.decode(refreshToken.getValue());
                ((DefaultOAuth2AccessToken)converted).setRefreshToken(refreshToken);
            }catch(IllegalArgumentException e) {
                LOG.debug("Existing refresh token is not a valid JWT, using the new generated refresh token",e);
            }

        }
        return converted;
    }   
}

All 15 comments

Hi,
Anyone facing similer issue ?

I noticed the same issue.

JwtAccessTokenConvertercreates a new refresh token and DefaultTokenServices just stores the old one (because reuse is turned on). The new refresh token is returned in response but doesn't work. In my understanding, the response should contain the old refresh token.

I am facing the same issuer. In my opinion It should not return a new refresh token when you are using refresh_token grant type.

Section 6. Refreshing an Access Token of 'The OAuth 2.0 Authorization Framework' RFC [6749] states:

The authorization server MAY issue a new refresh token, in which case the client MUST discard to old refresh token and replace it with the new refresh token. The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client. If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included by the client in the request.

Therefore, the issue is that the JwtAccessTokenConverter is complying with the standard, but the DefaultTokenServices is improperly saving the now revoked token. Without reviewing the code, it would appear that the JwtAccessTokenConverter is NOT testing the reuse flag before completing the refresh token request.

Facing the same issue. Any update on this?

Facing the same issue.

Possible duplicate of #1193 ?

It's a duplicate but the other bug was closed wrongly. This problem is still present in version 2.0.16.RELEASE of spring-security-oauth2.

Any update on this?

Facing the same issue in our production environment, any update on this would be helpful!

Facing the same issue.

This is similar to issue #1193.

The method 'JwtAccessTokenConverter.enhance' updates the refresh token 'ati' property with the token id('jti' property) from the new access token.

This update to the refresh token is not stored in the database in the 'DefaultTokenServices.refreshAccessToken' method due to the if statement 'if (!this.reuseRefreshToken) {' on line 123.

As @dfcoffin pointed out in the spec does in fact say that a new refresh token can be returned on refresh. In a sense we are reusing the existing refresh token as the expiry is not changing, which is not true in the other scenario where a completely new refresh token and updated expiry is generated.

I have created a PR with a fix for this issue: #1668

In the mean time I am excluding the dependency, and using jitpack to use my fix as a dependency instead.

The setReuseRefreshToken(true) does indeed work for JwtTokenStore. The confusion here is that the AccessTokenConverter.ATI claim in the OAuth2RefreshToken changes when an OAuth2AccessToken is refreshed.

The AccessTokenConverter.ATI claim in the OAuth2RefreshToken is a reference to the AccessTokenConverter.JTI claim in the OAuth2AccessToken. So when an access token is refreshed, it's assigned a new JTI, which than needs to be updated in the (original) refresh token in the ATI claim. This essentially changes the value of the refresh token. However, all other claims are reused as-is. I created a test (329b0ac) that proves this out.

I'm going to close this issue as works-as-designed.

I don't want to reopen this, but would anyone have any advice on making this work ?
I get the responsibility of the global behavior is not well defined, but my concern is : does that mean that in practice, I can't setup a "reuseRefreshToken"-like mechanism ?

I created this Class extending the JWTAccesTokenConverter that in case the existing refresh token is already a valid JWT it replaced it with the existing one

public class PreserveRefreshTokenJwtAcesTokenConverter extends JwtAccessTokenConverter implements TokenEnhancer {

    private static final Logger LOG =LoggerFactory.getLogger(PreserveRefreshTokenJwtAcesTokenConverter.class);

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        OAuth2AccessToken converted = super.enhance(accessToken, authentication);
        if(refreshToken!=null&& refreshToken.getValue()!=null) {
            //Preserve previous refresh token if it was already a valid JWT token.
            try {
                JwtHelper.decode(refreshToken.getValue());
                ((DefaultOAuth2AccessToken)converted).setRefreshToken(refreshToken);
            }catch(IllegalArgumentException e) {
                LOG.debug("Existing refresh token is not a valid JWT, using the new generated refresh token",e);
            }

        }
        return converted;
    }   
}
Was this page helpful?
0 / 5 - 0 ratings