Closed marcin-iwanow closed 1 month ago
@marcin-iwanow I'm not sure I see the value of hashing the returned refresh_token
. Can you please provide more details on how you see this as a benefit ?
FYI, validation is enforced during the refresh_token
grant flow, where the client originally granted is the only one allowed to use the refresh_token
.
Hi @jgrandja! That's my point actually: I would expect to have the refresh_token
returned in plaintext, even if I store it in it's hashed form on the DB. At this point it is the case only when the refresh_token
is not reused.
Let me know if that makes sense to you, I am still not sure I am clear enough :).
@marcin-iwanow
I would expect to have the
refresh_token
returned in plaintext
I'm still not following you. Have you reviewed the code for the OAuth2RefreshTokenGenerator
?
The generator produces an opaque refresh_token
and is returned as-is. Can you clarify on your expectation re: plaintext?
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
tl;dr
That's right, but when the refresh_token
is reused (i.e., registeredClient.getTokenSettings().isReuseRefreshTokens()
returns true
), the refresh token generator is not called in the refresh_token
grant flow, and instead the refresh_token
is returned as stored on the DB.
Deep dive into the flow:
the authenticate
method on OAuth2RefreshTokenAuthenticationProvider
is called, the refreshToken
within the authentication
argument comes from the client (so, is plaintext).
the Oauth2Authorization
is pulled from the DB using an implementation of the OAuth2AuthorizationService::findByToken method here . If the token is hashed on the DB, the implementation of findByToken
will reflect that (i.e., query the DB by the hashed value of the token). Though, as hash is one-way it cannot return the plaintext of the refresh token (*subject to workarounds like reusing the argument, but that's not really clean).
currentRefreshToken
is extracted from the Oauth2Authorization
returned by findByToken
in 2. and returned here upstream.
to sum up, if the token is reused (and only then!) we believe that the implementation does not support well one-way storage of tokens.
@marcin-iwanow I didn't get an answer to my question in the comment.
I don't see any added benefit of storing the refresh_token
in hashed form. I'm also not seeing any issues that may be caused with the current implementation.
I'm curious if other providers have implemented hashing of refresh_token
?
At this point, I'm going to close this based on the explanation provided.
@jgrandja thanks for looking into it!
Sorry, I misunderstood the question. Well, persistent refresh_token
is a very powerful secret so we (as well as other organisations that integrate SAS, actually) store it securely as recommended in RFC.
Expected Behavior It should be possible to store the refresh tokens hashed, also if the oauth2 client is configured to reuse the refresh tokens.
Current Behavior If the oauth2 client is reusing the refresh tokens, the refresh token returned in the refresh token flow is returned as stored in the DB. Consequently, if the tokens are stored hashed, subsequent attempts of executing the flow will be impossible.
In the code, this comes from the fact that the refresh token returned from
OAuth2RefreshTokenAuthenticationProvider:authenticate
is derived from the output value of theOAuth2AuthorizationService::findByToken
method.Example:
would return (if we for the sake of the example use SHA-1 for hashing):
Current workaround In the implementation of the
OAuth2AuthorizationService::findByToken
method, substitute the value of the token returned from the persistence layer with the argument, i.e., the plaintext token value.