kamermans / guzzle-oauth2-subscriber

OAuth 2.0 Client for Guzzle 4, 5, 6 and 7 with PHP 5.4 - PHP 8.0 - no more dependency hell!
MIT License
140 stars 31 forks source link

What is the best way to handle temporary failure on refreshing token? #21

Closed simensen closed 1 year ago

simensen commented 4 years ago

I am working with an OAuth server that is not super reliable and only has a 1 hour token life. This means that, usually within 24-36 hours, I will receive a failure response from the refresh call.

The current OAuth2Handler implementation will ask the TokenPersistenceInterface to delete a token that is expired. That token has the only copy of the refresh token. It looks like it gets exactly one shot to have that refresh call it work. If it works, great! It gives me a new token. If it does NOT work, I've now lost access to my refresh token.

Short of tweaking my TokenPersistenceInterface implementation to not actually delete a token when delete is called on it, are there any other options I'm missing?

kamermans commented 4 years ago

Hmm, good question @simensen, and that's a good point. We would indeed need the refresh token to retry getting an access token. In this scenario, you would normally get a new access token and refresh token by using the original credentials (in the case of client_credentials, this should be completely automatic).

You can probably accomplish this by using the ClosureTokenPersistence class to save the token data and not delete the old one on failures.

numerogeek commented 3 years ago

Hi @kamermans

We have the same problem : we have multiple instances of our app that call an API. All the instances shares the same access_token/refresh_token. Randomly, it appears that the renew process fails. So it delete the old token, and I end up having a bunch of errors because no token exists anymore. I suspect a concurrent access in the renewing process of the token because of multiple instance of worker are doing the same job. And to get a new access token, my provider needs a 2 factor authentication so it can't be completely automatic.

I was thinking about setting up a crontab whose job would be to force a refresh token every 3 hours while the token is normally valid for 12 hours. That would impeach the worker to renew the token, and getting in conflict with another worker that do the same job.

what do you think ? How can I do that ?

Thank you !

randebnw commented 3 years ago

Hi!

My solution was to use my own "OAuthSubscriber" class and override the getAccessToken method. I also detailed this issue here: https://github.com/kamermans/guzzle-oauth2-subscriber/issues/36

In this implementation, I removed the "deleteToken" call, so the old token is not deleted.

class MyCustomOAuth2Subscriber extends OAuth2Subscriber {
    /**
     * 
     * {@inheritDoc}
     * @see \kamermans\OAuth2\OAuth2Handler::getAccessToken()
     */
    public function getAccessToken() {
        // If token is not set try to get it from the persistent storage.
        if ($this->rawToken === null) {
            $this->rawToken = $this->tokenPersistence->restoreToken(call_user_func($this->newTokenSupplier));
        }

        // If token is not set or expired then try to acquire a new one...
        if ($this->rawToken === null || $this->rawToken->isExpired()) {
            // Hydrate `rawToken` with a new access token
            $this->requestNewAccessToken();

            // ...and save it.
            if ($this->rawToken) {
                $this->tokenPersistence->saveToken($this->rawToken);
            }
        }

        return $this->rawToken? $this->rawToken->getAccessToken(): null;
    }
}
kamermans commented 1 year ago

This should be fixed by #48