Environment details
- OS: Linux in Docker/Kubernetes
- PHP version: 8.3
- Package name and version: google/auth 1.53
Steps to reproduce
- When creating StorageClient, use config option 'authCache' => new \Google\Auth\Cache\FileSystemCacheItemPool('/tmp/some/dir/storage_auth')
- Use the StorageClient for a while until the authentication expires
- Witness 401 errors for storage actions
Checking the FileSystemCacheItemPool the root cause becomes clear:
serializing to disk does:
$serializedItem = serialize($item->get());
$result = file_put_contents($itemPath, $serializedItem, LOCK_EX);
This means that expiration value does not get written to disk at all, only the final value.
As a result when the value is asked from the cache with getItem call, it never fills expiration field.
TypedItem->isHit() returns true if expiration === null, making storage client use stale auth token.
Current implementations of MemoryCacheItemPool and SysVCacheItemPool retain the entire object and do not have this same issue.
Looking into CacheTrait the getCachedValue() has strong assumption that getItem() returns an object where isHit() call defines it valid item was found or not, it does not do any extra expiration checks etc.
This is the TypedItem->isHit() function:
public function isHit(): bool
{
if (!$this->isHit) {
return false;
}
if ($this->expiration === null) {
return true;
}
return $this->currentTime()->getTimestamp() < $this->expiration->getTimestamp();
}
In addition the CacheTrait->getCachedValue() function is missing explicit return null; as final line of function.
Environment details
Steps to reproduce
Checking the FileSystemCacheItemPool the root cause becomes clear:
serializing to disk does:
This means that expiration value does not get written to disk at all, only the final value.
As a result when the value is asked from the cache with getItem call, it never fills expiration field.
TypedItem->isHit() returns true if expiration === null, making storage client use stale auth token.
Current implementations of MemoryCacheItemPool and SysVCacheItemPool retain the entire object and do not have this same issue.
Looking into CacheTrait the getCachedValue() has strong assumption that getItem() returns an object where isHit() call defines it valid item was found or not, it does not do any extra expiration checks etc.
This is the TypedItem->isHit() function:
In addition the CacheTrait->getCachedValue() function is missing explicit return null; as final line of function.