Skip to content
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

Allow OAuth2Client refresh token to be updated when grant_type is "refresh_token" #151

Merged
merged 4 commits into from Feb 14, 2023

Conversation

jholzer-cciq
Copy link
Contributor

Update the OAuth2 base client implementation so that the RefreshToken property is updated when refreshing access tokens (grant_type = "refresh_token"). Previously, the refresh token was only updated for requests where grant_type was not equal to "refresh_token".

This change is being made because many OAuth2 implementations will include an updated refresh token in the response to a "refresh_token" request. For the implementations that don't, the RefreshToken property will continue to be null after the response has been processed.

Add another test to make sure that the client can handle the case where no refresh token is returned in a request where grant_type = "refresh_token"
@@ -206,8 +206,7 @@ private async Task QueryAccessTokenAsync(NameValueCollection parameters, Cancell
if (String.IsNullOrEmpty(AccessToken))
throw new UnexpectedResponseException(AccessTokenKey);

if (GrantType != "refresh_token")
RefreshToken = ParseTokenResponse(response.Content, RefreshTokenKey);
RefreshToken = ParseTokenResponse(response.Content, RefreshTokenKey);
Copy link
Collaborator

@niemyjski niemyjski Jan 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure about this change, can you dig into the oauth2 spec a bit more to see if it says anything about this. My thought process currently (without additional information which I'd love to have) is that we should parse it and set it if it returns anything. If it returns null than we leave what was there as it may only be set via the refresh_token grant type and we would be wiping that value out. But I see your point if the client always returns it, then this is the behavior we would want.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check out the specs and see what I can find. I can certainly put in a conditional that will only update RefreshToken if the refresh token value parsed from the response is non-null and non-whitespace (i.e. !string.IsNullOrWhitespace()).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the original OAuth2 spec (RFC 6749, Section 1.5), it states the following in regards to requesting a new access token using a refresh token:

(G) The client requests a new access token by authenticating with
the authorization server and presenting the refresh token. The
client authentication requirements are based on the client type
and on the authorization server policies.

(H) The authorization server authenticates the client and validates
the refresh token, and if valid, issues a new access token (and,
optionally, a new refresh token).

The spec clearly states that issuing a new refresh token is optional.

I still think the most reasonable default behavior is to just update the RefreshToken based on whatever you get back in the response. However, as I mentioned previously, I'm happy to make an additional change to only update RefreshToken in the case where an actual refresh_token is included in the response.

Ultimately, I think the best long term solution, that should eliminate any debate about dealing with refresh tokens, would be to have the OAuth2 Client's methods return result objects containing the access and refresh tokens, rather than storing/caching the "last" access and refresh tokens as properties. This design change also seems like it could open up the possibility of using OAuth2 clients as singletons, rather than having to create a new client instance every time tokens are needed for a different user.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@niemyjski any additional thoughts?

The spec is pretty clear that a new refresh token can be returned in response to a refresh request.

If you think the client should only update the RefreshToken property in the case where a new refresh token is returned, I can certainly make that change.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I've been pretty busy. I agree with your assessment and research about creating an object and making these stateless.

I think for now so it's not a breaking change (and so we have to check every single integration if it returns a refresh token or not), we try and parse the response for a refresh token if it's not null we set it, otherwise we leave it be.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@niemyjski I've updated the OAuth2 client so that RefreshToken will only be updated when a new value is provided in the response. I've updated unit tests as well.

OAuth2/Client/OAuth2Client.cs Outdated Show resolved Hide resolved
Co-authored-by: Blake Niemyjski <bniemyjski@gmail.com>
@niemyjski niemyjski merged commit 4a8767a into titarenko:master Feb 14, 2023
@niemyjski
Copy link
Collaborator

Thanks for the PR

@niemyjski
Copy link
Collaborator

New release is out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants