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

Sporadic "Invalid Credentials" Issues - Service Account #1075

Closed
strategyst opened this issue Oct 12, 2016 · 22 comments
Closed

Sporadic "Invalid Credentials" Issues - Service Account #1075

strategyst opened this issue Oct 12, 2016 · 22 comments
Assignees

Comments

@strategyst
Copy link

strategyst commented Oct 12, 2016

I seem to be having weird issues with "Invalid Credentials" errors, service accounts and the Search Console API. Sometimes everything works and I can access the API, then some time later I may try an access the SC API and I get an "Invalid Credentials" error. This is without changing any code.

Edit: Very weird. I swapped around my scopes and then it started working again. Nothing else was changed. Why would swapping my scopes around every so often work?

Here is what I am using:

class Google {

    private function googleApiAuthorise()
    {

        $client = new \Google_Client();
        putenv('GOOGLE_APPLICATION_CREDENTIALS=' . base_path() . '/keys/Tool-xxxxxxxxx.json');
        $client->useApplicationDefaultCredentials();
        $client->setSubject(example@example.iam.gserviceaccount.com');
        $client->setApplicationName("Tool");
        $scopes = ['https://www.googleapis.com/auth/webmasters.readonly', 'https://www.googleapis.com/auth/webmasters'];
        $client->setScopes($scopes);

        if( $client->isAccessTokenExpired()) {

            $client->refreshTokenWithAssertion();

        }

        return $client;

    }

    public function getSearchAnalytics($search_type = 'web')
    {

        $authorise = Google::googleApiAuthorise();

        $service = new \Google_Service_Webmasters($authorise);

        $request = new \Google_Service_Webmasters_SearchAnalyticsQueryRequest;

        $request->setStartRow(0);
        $request->setStartDate('2016-06-01');
        $request->setEndDate(Carbon::now()->toDateString());
        $request->setSearchType($search_type);
        $request->setRowLimit(200);
        $request->setDimensions(array('date','page','country','device','query'));

        $query_search = $service->searchanalytics->query("http://www.example.com/", $request); 
        $rows = $query_search->getRows();

        return $rows;

    }
}

The weird thing is it works sometimes. Is this some sort of bug? I've followed pretty much every tutorial out there about service accounts and this API client, but it still seems to be quite random as to why sometimes I can use the API and other times I get an "Invalid Credentials" error.

@strategyst strategyst changed the title Sporadic "Invalid Credentials" Issues - Service Account Google API Client - Sporadic "Invalid Credentials" Issues - Service Account Oct 13, 2016
@strategyst strategyst changed the title Google API Client - Sporadic "Invalid Credentials" Issues - Service Account Sporadic "Invalid Credentials" Issues - Service Account Oct 13, 2016
@dkrausahma
Copy link

dkrausahma commented Oct 15, 2016

When you say "sporadic" do you mean after 3600 seconds? That would be the time when the access token is expired and you would have to get a new one.

I am having the same issue and i do understand that it is due to the expired access token. This is my particular code sequence (which is quite similar to yours):

    /* This env variable holds the path to the keyfile with the JSON based certificate. It will be required by the Google API Client */
    putenv( 'GOOGLE_APPLICATION_CREDENTIALS=' . $objApplication->getConfig( 'App::GoogleAuthServiceAccoutKeyAsJson' ) );
    try{

        /* Using Stash for caching */
        //$objCache = new Pool( new FileSystem );

        //$objCacheLogger = new Logger;
        //$hdTokenCallback = function( $cacheKey, $accessToken ) use ( $objCacheLogger ) {
        //    $objCacheLogger->debug( sprintf( 'A new access token \'%s\' has beeen received for the cache key %s', $accessToken, $cacheKey ) );
        //};

        /* Creating the client */
        $objGoogleApiClient = new \Google_Client();
        $objGoogleApiClient->useApplicationDefaultCredentials();
        $objGoogleApiClient->setApplicationName( 'myTestApplication' );
        $objGoogleApiClient->setAccessType( 'offline' );
        $objGoogleApiClient->setScopes( [ 'https://www.googleapis.com/auth/webmasters.readonly' ] );
        //$objGoogleApiClient->setCache( $objCache );
        //$objGoogleApiClient->setTokenCallback( $hdTokenCallback );

        /* Refresh token when expired */
        if( $objGoogleApiClient->isAccessTokenExpired() ){
            $objGoogleApiClient->refreshTokenWithAssertion();
        }

        /* Creating the actual service */
        $this->objGoogleService = new \Google_Service_Webmasters( $objGoogleApiClient );

    } catch( \Google_Exception $objException ){
        die(" An exception has occured!" );
    }

I have also diggged into what feels like the entire documentation there is in the Internet ;-). But i still get the expiration after 3600. I am as well using a service account and this code runs on the console.

What am i doing wrong, why am getting the expiration? Is this part

        /* Refresh token when expired */
        if( $objGoogleApiClient->isAccessTokenExpired() ){
            $objGoogleApiClient->refreshTokenWithAssertion();
        }

not enough to do the refreshing? I used a debugger but could not find a refresh token passed back to Client.php, it was always null. Recreated millions of service accounts and went through the initial authentication process expecting to get one, but none which i could save away and use for later refreshing process.

As opposed to omnicodagithub i am getting the 401 not sporadically but regularly after one hour.

Above code is using google/apiclient v2.0.3.

Thanks for any advice.

@strategyst
Copy link
Author

It was the expiration. I've decided to go with OAuth instead but that's also seeming to be a nightmare to set up. Even when I copy Google's own examples they often don't seem to work or only work temporarily.

@dkrausahma
Copy link

dkrausahma commented Oct 16, 2016

-- Even when I copy Google's own examples they often don't seem to work or only work temporarily
Which is why i want to solve this approach here now because i already spent too much time trying to resolve this. Version 1 of the API worked fine (at least for the Search Console) but i was missing the setStartRow() for result sets exceeding 5000 rows in the old version. Along with the composer support it makes sense for me to update to version 2.

@dkrausahma
Copy link

Update:
Just for the heck of it i downgraded the Google Api Client to v.2.0.2 and am realizing that above code works perfectly, also after 3600 seconds have been exceeded. Refreshing the access token works just fine. So without having gone into depth the mentioned misbehaviour only occurs with the Api Client in v.2.0.3.

@0bp
Copy link

0bp commented Oct 17, 2016

We have the same issue with v2.0.3

if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($refreshToken);
}

We see expired access token and ~30 seconds after fetching a new access token the API responds with "Invalid Credentials".

Re-initialising the client when the access token expires works as a current "dirty but worky" solution.

@chonthu
Copy link

chonthu commented Oct 17, 2016

@omnicodagithub I had the same issue, but I think I have a solution, @bshaffer does some checks for cache in the library prebuilt, can you try this?

  $cache = new Stash\Pool(new Stash\Driver\FileSystem); // or whatever
  $client->setCache($cache);

   if( $client->isAccessTokenExpired() ){
        $cache->clear();
         $client->refreshTokenWithAssertion();
   }

@bshaffer
Copy link
Contributor

I am looking into this. Can you provide a stack trace for the Invalid Credentials error?

I believe the problem may be the refresh token is not being persisted in the in-memory cache.

@bshaffer
Copy link
Contributor

Changing the scopes uses a different cache key. This is almost certainly why that fixes the issue. But if you're using in-memory cache, I would think the tokens wouldn't persist enough for it to matter.

@LindaLawton
Copy link

Links to another question on stack along the same lines. Google Analytics Reporting API V4 with “Request had invalid authentication credentials”

@strategyst
Copy link
Author

Just an update from me. So to test I am storing the entire access token in the database (access_key, token_type, expires_in, refresh_key and created). I am using this token from the database with the setAccessToken method. This is almost always resulting in a 401 "Invalid Credentials" error. When I test this same key in the Google API Playground it works fine, so it doesn't seem to be the access token.

Not sure if I'm not quite understanding it, but if I'm using an access token from the database, then the cache shouldn't affect this should it? By the way, it did actually work earlier for a while using this method. This issue really has me scratching my head as one time it's working fine, the next it's not working.

@bshaffer
Copy link
Contributor

bshaffer commented Oct 18, 2016

Thanks for the update @omnicodagithub ! Ahh how the plot thickens.

Could you do two things for me please?

  1. Comment here with a stack trace.
  2. Use Charles Proxy to obtain the HTTP request of the call, and ensure that the requests going to www.googleapis.com/auth (to retrieve access tokens) and the requests going to www.googleapis.com/API_NAME (to make the api call) match what you'd expect, i.e. the client credentials, access token from the database, etc.

Set up the proxy by adding this when you create your client:

$options = [
    'proxy' => 'localhost:8888', // the default for charles
    'verify' => false,
];
$httpClient = new GuzzleHttp\Client($options);
$client->setHttpClient($httpClient);

@strategyst
Copy link
Author

Hi @bshaffer! Thanks for the help by the way.

Ok, so here is the stack trace:

Stack trace:
#0 C:\xampp\htdocs\marketingtool\vendor\google\apiclient\src\Google\Http\REST.php(94): Google_Http_REST::decodeHttpResponse(Object(GuzzleHttp\Psr7\Response), Object(GuzzleHttp\Psr7\Request), 'Google_Service_...')
#1 [internal function]: Google_Http_REST::doExecute(Object(GuzzleHttp\Client), Object(GuzzleHttp\Psr7\Request), 'Google_Service_...')
#2 C:\xampp\htdocs\marketingtool\vendor\google\apiclient\src\Google\Task\Runner.php(181): call_user_func_array(Array, Array)
#3 C:\xampp\htdocs\marketingtool\vendor\google\apiclient\src\Google\Http\REST.php(58): Google_Task_Runner->run()
#4 C:\xampp\htdocs\marketingtool\vendor\google\apiclient\src\Google\Client.php(781): Google_Http_REST::execute(Object(GuzzleHttp\Client), Object(GuzzleHttp\Psr7\Request), 'Google_Service_...', Array)
#5 C:\xampp\htdocs\marketingtool\vendor\google\apiclient\src\Google\Service\Resource.php(232): Google_Client->execute(Object(GuzzleHttp\Psr7\Request), 'Google_Service_...')
#6 C:\xampp\htdocs\marketingtool\vendor\google\apiclient-services\src\Google\Service\Webmasters\Resource\Searchanalytics.php(48): Google_Service_Resource->call('query', Array, 'Google_Service_...')
#7 C:\xampp\htdocs\marketingtool\app\Http\Controllers\SearchConsoleController.php(146): Google_Service_Webmasters_Resource_Searchanalytics->query('http://www.omni...', Object(Google_Service_Webmasters_SearchAnalyticsQueryRequest))
#8 [internal function]: App\Http\Controllers\SearchConsoleController->test()
#9 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Controller.php(55): call_user_func_array(Array, Array)
#10 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\ControllerDispatcher.php(44): Illuminate\Routing\Controller->callAction('test', Array)
#11 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Route.php(190): Illuminate\Routing\ControllerDispatcher->dispatch(Object(Illuminate\Routing\Route), Object(App\Http\Controllers\SearchConsoleController), 'test')
#12 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Route.php(144): Illuminate\Routing\Route->runController()
#13 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Router.php(642): Illuminate\Routing\Route->run(Object(Illuminate\Http\Request))
#14 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(53): Illuminate\Routing\Router->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#15 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Middleware\SubstituteBindings.php(41): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#16 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(137): Illuminate\Routing\Middleware\SubstituteBindings->handle(Object(Illuminate\Http\Request), Object(Closure))
#17 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(33): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#18 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php(43): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#19 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(137): Illuminate\Auth\Middleware\Authenticate->handle(Object(Illuminate\Http\Request), Object(Closure))
#20 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(33): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#21 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken.php(65): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#22 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(137): Illuminate\Foundation\Http\Middleware\VerifyCsrfToken->handle(Object(Illuminate\Http\Request), Object(Closure))
#23 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(33): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#24 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\View\Middleware\ShareErrorsFromSession.php(49): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#25 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(137): Illuminate\View\Middleware\ShareErrorsFromSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#26 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(33): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#27 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Session\Middleware\StartSession.php(64): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#28 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(137): Illuminate\Session\Middleware\StartSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#29 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(33): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#30 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse.php(37): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#31 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(137): Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse->handle(Object(Illuminate\Http\Request), Object(Closure))
#32 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(33): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#33 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Cookie\Middleware\EncryptCookies.php(59): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#34 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(137): Illuminate\Cookie\Middleware\EncryptCookies->handle(Object(Illuminate\Http\Request), Object(Closure))
#35 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(33): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#36 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(104): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#37 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Router.php(644): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#38 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Router.php(618): Illuminate\Routing\Router->runRouteWithinStack(Object(Illuminate\Routing\Route), Object(Illuminate\Http\Request))
#39 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Router.php(596): Illuminate\Routing\Router->dispatchToRoute(Object(Illuminate\Http\Request))
#40 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php(267): Illuminate\Routing\Router->dispatch(Object(Illuminate\Http\Request))
#41 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(53): Illuminate\Foundation\Http\Kernel->Illuminate\Foundation\Http{closure}(Object(Illuminate\Http\Request))
#42 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode.php(46): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#43 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(137): Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode->handle(Object(Illuminate\Http\Request), Object(Closure))
#44 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php(33): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#45 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(104): Illuminate\Routing\Pipeline->Illuminate\Routing{closure}(Object(Illuminate\Http\Request))
#46 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php(149): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#47 C:\xampp\htdocs\marketingtool\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php(116): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#48 C:\xampp\htdocs\marketingtool\public\index.php(54): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate\Http\Request))
#49 {main}

Regarding point two, when using Charles I can see that a different access_token is being used for authorisation. The one I am using for setAccessToken() is definitely the correct one (I'm printing it out to the page so I can confirm). Is there a cached one and is that one being used instead of the one I'm passing to setAccessToken()?

@bshaffer
Copy link
Contributor

bshaffer commented Oct 19, 2016

@omnicodagithub hmm, if you've told the Google_Client object to use application default credentials, it will use those instead of the access token. In your code above you call useApplicationDefaultCredentials. Are you calling $client->setAccessToken in the same call?

Digging deeper into this, it appears that the explicitly-set Access Token bypasses the cache, and so shouldn't be affected.

Is it possible you're using two different service accounts? Have you tried rolling back to v2.0.2 as suggested above?

@chonthu
Copy link

chonthu commented Oct 19, 2016

@bshaffer looking at the github compare with a previous stable version; It seems cache is set always, in memory or filesystem if pool library is available

This is why clearing works, since when you clear it makes a brand new token refresh call (which defeats the whole purpose of cache)

Hope this was helpful, maybe I have some time tomorrow I can submit a pull request for the line to check if whats in cache is expired first before using it

@strategyst
Copy link
Author

strategyst commented Oct 19, 2016

@bshaffer Hmm weird. I was using useApplicationDefaultCredentialsbefore, but I stopped using it days ago. I was originally using service accounts, but I decided instead to go with just OAauth. I'm using setAuthConfig, but that only looks as though it uses useApplicationDefaultCredentials if the JSON file has a type of 'service_account'.

I added this last night, just to test, so it could be this as the reason for useApplicationDefaultCredentials:

if( $client->isAccessTokenExpired() ){
        $cache->clear();
         $client->refreshTokenWithAssertion();
   }

Edit: I did what @chonthu suggested and I just cleared the cache. I started getting results again after that.

@bshaffer
Copy link
Contributor

You shouldn't need to manually clear the cache. This implies the cache is not invalidated properly. Looking in the code, a token being received should expire after 1500 seconds.

Can you call var_dump(get_class($client->getCache())); and tell me what you get?

@bshaffer
Copy link
Contributor

To fix this in the auth library, I've added googleapis/google-auth-library-php#138, which is not ideal (the call should be unnecessary) but will get the job done.

Another option would be to stop using tedivm/stash by default, which is what we should have done to start, but it would break B.C.

@mikegioia
Copy link

I just wanted to mention that we're still seeing this error in 2.0.3 (as well as the last 1.x release). The only thing that worked was manually fetching a new access token, as mentioned above. My hack is to do that on the first 401 and retry the API call but I would obviously prefer if this just worked.

Please let me know if there's anything you'd like me to provide. I can send stack traces, and I'm using the Calendar API.

BVMiko added a commit to BVMiko/google-auth-library-php that referenced this issue May 23, 2017
Per PSR-6:

> Hit - A cache hit occurs when a Calling Library requests an Item by key and a matching value is found for that key, and that value has not expired, and the value is not invalid for some other reason. Calling Libraries SHOULD make sure to verify isHit() on all get() calls.

The $cacheItem response is never validated, and in the case of the Stash library the response contains the expired data (presumably to provide access to the previous cache).

This should fix a recurring issue with authentication when using Stash for storing tokens; which were never being regenerated properly.  There is one open issue googleapis/google-api-php-client#1075, plus several other closed issues which appear to have misdiagnosed the issue.
@BVMiko
Copy link

BVMiko commented May 23, 2017

I ran into this problem and have been working around it for months; finally got around to tracing down this bug. The Stash library's response appears to include expired cache data which is properly flagged as a Miss (which could be a benefit for some situations), however the Google Auth library never tests it to confirm it should be used. This pull request does a proper verification of the response; and allows us to replace hacks like the one suggested in October by @chonthu (which was clearing the entire Stash cache on every hit). My client setup code now no longer needs to test for expiration or manually refresh the tokens.

@bshaffer
Copy link
Contributor

@BVMiko while i agree with this fix because of the quote from PSR-6, it looks like stash ensures the isHit property is false before returning anything with get, so that either way the function returns null (see here). So at least with the Stash implementation, I do not see how this will fix the issues described.

@bshaffer
Copy link
Contributor

Looks like I added that 8 months ago...

@bshaffer
Copy link
Contributor

Okay, I just figured out what's going on. Two weeks before your PR, a new version of Stash was finally tagged with the fix I added 8 months ago. So this is now fixed with v0.14.2.

Since the PSR-6 spec says the calling library SHOULD verify the cache hit, the changes in #150 are valid, even though the spec ALSO says get should return NULL if there is no hit (making the call to isHit unnecessary).

Either way this issue is fixed!

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

No branches or pull requests

8 participants