kamermans/guzzle-oauth2-subscriber

Using refresh token grant type for 2-legged OAuth

Opened this issue · 4 comments

We are trying to make the middleware work with a 2-legged OAuth flow. The server we are connecting to has already authorized our app, and we've been issued a refresh token (which doesn't seem to expire) that we can use to get new access tokens.

However, there's probably something I'm missing or the middleware is not quite designed for this.

I've made it work in the end, but perhaps there's a better way?

Here's my code:

$config = [
    'client_id' => 'ABC',
    'client_secret' => 'DEF',
    'refresh_token' => 'GHI'
];

$clientCredentialsGrantType = new ClientCredentials($refreshClient, $config);
$refreshTokenGrantType = new RefreshToken($refreshClient, $config);
            
$oauth = new OAuth2Middleware($clientCredentialsGrantType, $refreshTokenGrantType);
$oauth->setAccessToken([
    'refresh_token' => $config['refresh_token'],
    'expires_in' => -1
]);

Here I'm tricking the middleware it has acquired an access token which has expired, so that these conditions are met:

  • Inside OAuth2Handler::requestNewAccessToken I'm learning that a raw token must exist:
if ($this->refreshTokenGrantType && $this->rawToken && $this->rawToken->getRefreshToken()) {
  • Next in OAuth2Handler::getAccessToken:
if ($this->rawToken === null || $this->rawToken->isExpired()) {

For the expiration check to be evaluated to true, I'm setting the expiration to a date in the past via expires_in - it can't be the default 0, since isExpired won't allow it:

public function isExpired()
{
    return $this->expiresAt && $this->expiresAt < time();
}

Hi @bogdanghervan, thanks for the question and context. Your assessment seems correct to me on all fronts, and I agree that there's probably a better way (particularly because your implementation makes assumptions about the library that could reasonably change).

I think this scenario is made unnecessarily complicated by the fact that you are not using a persistence provider, nor are you obtaining the initial key and refresh token using the library. In a more typical scenario, you would use a persistence provider and then use the library to get the initial token, then, when it expires, the refresh token would be used to retrieve a newer token, which would be saved to disk (or redis or something). This pattern would continue indefinitely until the refresh token was revoked. Also, by using persistence, you would be able to reuse the same token until it expires, instead of issuing a new token each time.

Are you able to share the name of a provider that behaves in this way (issuing a bad token with a good refresh token, or requiring your first token to be retrieved using a refresh token)? It would be good to understand how common this paradigm is.

I'm happy to pull in support for this type of oauth flow if you have an implementation pull request or suggestions. I'm not sure how practical it would be for me to write it without a way to test it, however.

@kamermans
I'm dealing with exactly the same issue. Thank you @bogdanghervan for the "fix".

Glad to share more details in PM.
2019-10-28_21-46-31

@kamermans Could you provide an example on how to do this? Seems like it is a less "fragile" approach to fixing the problem

I think this scenario is made unnecessarily complicated by the fact that you are not using a persistence provider, nor are you obtaining the initial key and refresh token using the library. In a more typical scenario, you would use a persistence provider and then use the library to get the initial token, then, when it expires, the refresh token would be used to retrieve a newer token, which would be saved to disk (or redis or something). This pattern would continue indefinitely until the refresh token was revoked. Also, by using persistence, you would be able to reuse the same token until it expires, instead of issuing a new token each time.

@bogdanghervan Can you post or send the full working example for your solution?

Thanks!