Skip to content

Commit 2b671b7

Browse files
feat(Auth): add StaticCredentials for a pre-issued access token (#9677)
1 parent 0a7060d commit 2b671b7

2 files changed

Lines changed: 176 additions & 0 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
2+
/*
3+
* Copyright 2026 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace Google\Auth\Credentials;
19+
20+
use Google\Auth\FetchAuthTokenInterface;
21+
use InvalidArgumentException;
22+
23+
/**
24+
* Provides a set of credentials that always returns a pre-issued access token.
25+
*
26+
* No token exchange or HTTP request is ever performed; the supplied token is
27+
* returned as-is. This is useful for development and testing, or when an access
28+
* token has already been obtained through some other means and simply needs to
29+
* be handed to the auth middleware.
30+
*
31+
* ```
32+
* use Google\Auth\Credentials\StaticCredentials;
33+
* use Google\Auth\Middleware\AuthTokenMiddleware;
34+
*
35+
* $creds = new StaticCredentials('ya29.my-access-token');
36+
* $middleware = new AuthTokenMiddleware($creds);
37+
* ```
38+
*/
39+
class StaticCredentials implements FetchAuthTokenInterface
40+
{
41+
/**
42+
* @var array{access_token: string, expires_at: int}
43+
*/
44+
private array $token;
45+
46+
/**
47+
* @param string $accessToken The pre-issued OAuth2 access token to use.
48+
* @param int $expiresIn [optional] The lifetime of the token in seconds, used
49+
* to populate its expiration. Defaults to 3600 (one hour).
50+
*/
51+
public function __construct(string $accessToken, int $expiresIn = 3600)
52+
{
53+
if ($accessToken === '') {
54+
throw new InvalidArgumentException('The access token must not be empty');
55+
}
56+
57+
$this->token = [
58+
'access_token' => $accessToken,
59+
'expires_at' => time() + $expiresIn,
60+
];
61+
}
62+
63+
/**
64+
* Returns the static access token supplied to the constructor.
65+
*
66+
* @param callable|null $httpHandler Unused; present for interface compatibility.
67+
* @return array{access_token: string, expires_at: int}
68+
*/
69+
public function fetchAuthToken(?callable $httpHandler = null)
70+
{
71+
return $this->token;
72+
}
73+
74+
/**
75+
* Returns a cache key derived from the supplied token. Caching a static token
76+
* has no real benefit, but a stable, token-specific key ensures a shared cache
77+
* pool never returns another credential's token.
78+
*
79+
* @return string
80+
*/
81+
public function getCacheKey()
82+
{
83+
return 'static_credentials_' . md5($this->token['access_token']);
84+
}
85+
86+
/**
87+
* @return array{access_token: string, expires_at: int}
88+
*/
89+
public function getLastReceivedToken()
90+
{
91+
return $this->token;
92+
}
93+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
2+
/*
3+
* Copyright 2026 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace Google\Auth\Tests\Credentials;
19+
20+
use Google\Auth\Credentials\StaticCredentials;
21+
use InvalidArgumentException;
22+
use PHPUnit\Framework\TestCase;
23+
24+
/**
25+
* @group credentials
26+
* @group credentials-static
27+
*/
28+
class StaticCredentialsTest extends TestCase
29+
{
30+
public function testFetchAuthTokenReturnsSuppliedToken()
31+
{
32+
$creds = new StaticCredentials('my-access-token');
33+
$token = $creds->fetchAuthToken();
34+
35+
$this->assertEquals('my-access-token', $token['access_token']);
36+
$this->assertGreaterThan(time(), $token['expires_at']);
37+
}
38+
39+
public function testFetchAuthTokenHonorsExpiresIn()
40+
{
41+
$before = time();
42+
$creds = new StaticCredentials('my-access-token', 120);
43+
$token = $creds->fetchAuthToken();
44+
45+
$this->assertGreaterThanOrEqual($before + 120, $token['expires_at']);
46+
$this->assertLessThanOrEqual(time() + 120, $token['expires_at']);
47+
}
48+
49+
public function testFetchAuthTokenIgnoresHttpHandler()
50+
{
51+
$creds = new StaticCredentials('my-access-token');
52+
$handlerCalled = false;
53+
$httpHandler = function () use (&$handlerCalled) {
54+
$handlerCalled = true;
55+
};
56+
57+
$creds->fetchAuthToken($httpHandler);
58+
$this->assertFalse($handlerCalled);
59+
}
60+
61+
public function testGetLastReceivedTokenMatchesFetch()
62+
{
63+
$creds = new StaticCredentials('my-access-token');
64+
$this->assertEquals($creds->fetchAuthToken(), $creds->getLastReceivedToken());
65+
}
66+
67+
public function testGetCacheKeyIsStableAndTokenSpecific()
68+
{
69+
$creds = new StaticCredentials('my-access-token');
70+
$other = new StaticCredentials('another-token');
71+
72+
$this->assertEquals($creds->getCacheKey(), (new StaticCredentials('my-access-token'))->getCacheKey());
73+
$this->assertNotEquals($creds->getCacheKey(), $other->getCacheKey());
74+
}
75+
76+
public function testEmptyAccessTokenThrows()
77+
{
78+
$this->expectException(InvalidArgumentException::class);
79+
$this->expectExceptionMessage('The access token must not be empty');
80+
81+
new StaticCredentials('');
82+
}
83+
}

0 commit comments

Comments
 (0)