Skip to content

Commit

Permalink
feat: add ID tokens for user refresh credentials (#468)
Browse files Browse the repository at this point in the history
  • Loading branch information
bshaffer authored Oct 31, 2024
1 parent da1f02a commit 1601efc
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 124 deletions.
11 changes: 5 additions & 6 deletions src/ApplicationDefaultCredentials.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Google\Auth\Credentials\AppIdentityCredentials;
use Google\Auth\Credentials\GCECredentials;
use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Auth\Credentials\UserRefreshCredentials;
use Google\Auth\HttpHandler\HttpClientCache;
use Google\Auth\HttpHandler\HttpHandlerFactory;
use Google\Auth\Middleware\AuthTokenMiddleware;
Expand Down Expand Up @@ -299,14 +300,12 @@ public static function getIdTokenCredentials(
}

if ($jsonKey['type'] == 'authorized_user') {
throw new InvalidArgumentException('ID tokens are not supported for end user credentials');
}

if ($jsonKey['type'] != 'service_account') {
$creds = new UserRefreshCredentials(null, $jsonKey, $targetAudience);
} elseif ($jsonKey['type'] == 'service_account') {
$creds = new ServiceAccountCredentials(null, $jsonKey, null, $targetAudience);
} else {
throw new InvalidArgumentException('invalid value in the type field');
}

$creds = new ServiceAccountCredentials(null, $jsonKey, null, $targetAudience);
} elseif (self::onGce($httpHandler, $cacheConfig, $cache)) {
$creds = new GCECredentials(null, null, $targetAudience);
$creds->setIsOnGce(true); // save the credentials a trip to the metadata server
Expand Down
38 changes: 29 additions & 9 deletions src/Credentials/UserRefreshCredentials.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
use Google\Auth\CredentialsLoader;
use Google\Auth\GetQuotaProjectInterface;
use Google\Auth\OAuth2;
use InvalidArgumentException;
use LogicException;

/**
* Authenticates requests using User Refresh credentials.
Expand Down Expand Up @@ -55,48 +57,67 @@ class UserRefreshCredentials extends CredentialsLoader implements GetQuotaProjec
*/
protected $quotaProject;

/**
* Whether this is an ID token request or an access token request. Used when
* building the metric header.
*/
private bool $isIdTokenRequest = false;

/**
* Create a new UserRefreshCredentials.
*
* @param string|string[] $scope the scope of the access request, expressed
* @param string|string[]|null $scope the scope of the access request, expressed
* either as an Array or as a space-delimited String.
* @param string|array<mixed> $jsonKey JSON credential file path or JSON credentials
* as an associative array
* @param string|null $targetAudience The audience for the ID token.
*/
public function __construct(
$scope,
$jsonKey
$jsonKey,
string $targetAudience = null
) {
if (is_string($jsonKey)) {
if (!file_exists($jsonKey)) {
throw new \InvalidArgumentException('file does not exist');
throw new InvalidArgumentException('file does not exist or is unreadable');
}
$json = file_get_contents($jsonKey);
if (!$jsonKey = json_decode((string) $json, true)) {
throw new \LogicException('invalid json for auth config');
throw new LogicException('invalid json for auth config');
}
}
if (!array_key_exists('client_id', $jsonKey)) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'json key is missing the client_id field'
);
}
if (!array_key_exists('client_secret', $jsonKey)) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'json key is missing the client_secret field'
);
}
if (!array_key_exists('refresh_token', $jsonKey)) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'json key is missing the refresh_token field'
);
}
if ($scope && $targetAudience) {
throw new InvalidArgumentException(
'Scope and targetAudience cannot both be supplied'
);
}
$additionalClaims = [];
if ($targetAudience) {
$additionalClaims = ['target_audience' => $targetAudience];
$this->isIdTokenRequest = true;
}
$this->auth = new OAuth2([
'clientId' => $jsonKey['client_id'],
'clientSecret' => $jsonKey['client_secret'],
'refresh_token' => $jsonKey['refresh_token'],
'scope' => $scope,
'tokenCredentialUri' => self::TOKEN_CREDENTIAL_URI,
'additionalClaims' => $additionalClaims,
]);
if (array_key_exists('quota_project_id', $jsonKey)) {
$this->quotaProject = (string) $jsonKey['quota_project_id'];
Expand All @@ -122,10 +143,9 @@ public function __construct(
*/
public function fetchAuthToken(?callable $httpHandler = null, array $metricsHeader = [])
{
// We don't support id token endpoint requests as of now for User Cred
return $this->auth->fetchAuthToken(
$httpHandler,
$this->applyTokenEndpointMetrics($metricsHeader, 'at')
$this->applyTokenEndpointMetrics($metricsHeader, $this->isIdTokenRequest ? 'it' : 'at')
);
}

Expand Down
3 changes: 3 additions & 0 deletions src/OAuth2.php
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,9 @@ public function generateCredentialsRequest(?callable $httpHandler = null, $heade
break;
case 'refresh_token':
$params['refresh_token'] = $this->getRefreshToken();
if (isset($this->getAdditionalClaims()['target_audience'])) {
$params['target_audience'] = $this->getAdditionalClaims()['target_audience'];
}
$this->addClientCredentials($params);
break;
case self::JWT_URN:
Expand Down
Loading

0 comments on commit 1601efc

Please sign in to comment.