Skip to content

Revoke previous refresh token after issuing a new one #1128

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

Closed
proyupgrade opened this issue Mar 16, 2023 · 3 comments
Closed

Revoke previous refresh token after issuing a new one #1128

proyupgrade opened this issue Mar 16, 2023 · 3 comments
Assignees
Labels
status: invalid An issue that we don't feel is valid

Comments

@proyupgrade
Copy link

Expected Behavior
Refresh token used on the refresh_token grant type flow should be invalidated after the new one has been issued.

Current Behavior
Previously used refresh token can still be used even though the server generates a new one.

Context
According to the specification (https://www.rfc-editor.org/rfc/rfc6749#section-5.2):

The authorization server MAY revoke the old
refresh token after issuing a new refresh token to the client.

At the moment, it is only possible to change the server's configuration to make it issue a new refresh token on each request instead of reusing the same one over again. We're looking for a similar approach that aims to revoke the previous token at the same time to provide better security.

@proyupgrade proyupgrade added the type: enhancement A general enhancement label Mar 16, 2023
@jcranfordupgrade
Copy link

jcranfordupgrade commented Mar 23, 2023

I found this in RFC 7009 OAuth 2.0 Token Revocation.

  1. Revoke refresh_token SHOULD revoke related access_token(s).
  2. Revoke access_token MAY revoke related refresh_token(s).

For OIDC ID token, I think we can infer similar.

https://www.rfc-editor.org/rfc/rfc7009#section-2.1

   Depending on the authorization server's revocation policy, the
   revocation of a particular token may cause the revocation of related
   tokens and the underlying authorization grant.  If the particular
   token is a refresh token and the authorization server supports the
   revocation of access tokens, then the authorization server SHOULD
   also invalidate all access tokens based on the same authorization
   grant (see Implementation Note).  If the token passed to the request
   is an access token, the server MAY revoke the respective refresh
   token as well.

@jgrandja
Copy link
Collaborator

@proyupgrade

Previously used refresh token can still be used even though the server generates a new one.

This is not correct. If the AS generates a new refresh token then the previous one cannot be reused. Add the test below to OAuth2RefreshTokenGrantTests as it demonstrates that the old refresh token cannot be used.

@Test
public void requestWhenGenerateNewRefreshTokenOnRefreshThenOldOneCannotBeUsed() throws Exception {
	this.spring.register(AuthorizationServerConfiguration.class).autowire();

	TokenSettings tokenSettings = TokenSettings.builder().reuseRefreshTokens(false).build();
	RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
			.tokenSettings(tokenSettings)
			.build();
	this.registeredClientRepository.save(registeredClient);

	OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
	this.authorizationService.save(authorization);

	OAuth2RefreshToken originalRefreshToken = authorization.getRefreshToken().getToken();

	MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
	parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue());
	parameters.set(OAuth2ParameterNames.REFRESH_TOKEN, originalRefreshToken.getTokenValue());

	this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
					.params(parameters)
					.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
							registeredClient.getClientId(), registeredClient.getClientSecret())))
			.andExpect(status().isOk());

	OAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId());

	// Assert new refresh token was generated
	assertThat(updatedAuthorization.getRefreshToken().getToken().getTokenValue()).isNotEqualTo(originalRefreshToken.getTokenValue());

	// Attempt again using the original refresh token
	this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
					.params(parameters)
					.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
							registeredClient.getClientId(), registeredClient.getClientSecret())))
			.andExpect(status().isBadRequest());		// Old refresh token cannot be used
}

@jgrandja jgrandja self-assigned this Mar 27, 2023
@jgrandja jgrandja added status: invalid An issue that we don't feel is valid and removed type: enhancement A general enhancement labels Mar 27, 2023
@proyupgrade
Copy link
Author

Apologies, it works indeed. There was an issue on how we handle our persistence; which made the refresh token always reusable.

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

3 participants