2022-12-15 17:34:08 +01:00
< ? php
/*
* Copyright 2015 Google Inc .
*
* Licensed under the Apache License , Version 2.0 ( the " License " );
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http :// www . apache . org / licenses / LICENSE - 2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
namespace Google\Site_Kit_Dependencies\Google\Auth ;
2025-02-28 08:42:11 +01:00
use Google\Site_Kit_Dependencies\Firebase\JWT\JWT ;
use Google\Site_Kit_Dependencies\Firebase\JWT\Key ;
2022-12-15 17:34:08 +01:00
use Google\Site_Kit_Dependencies\Google\Auth\HttpHandler\HttpClientCache ;
use Google\Site_Kit_Dependencies\Google\Auth\HttpHandler\HttpHandlerFactory ;
use Google\Site_Kit_Dependencies\GuzzleHttp\Psr7\Query ;
use Google\Site_Kit_Dependencies\GuzzleHttp\Psr7\Request ;
use Google\Site_Kit_Dependencies\GuzzleHttp\Psr7\Utils ;
use InvalidArgumentException ;
use Google\Site_Kit_Dependencies\Psr\Http\Message\RequestInterface ;
use Google\Site_Kit_Dependencies\Psr\Http\Message\ResponseInterface ;
use Google\Site_Kit_Dependencies\Psr\Http\Message\UriInterface ;
/**
* OAuth2 supports authentication by OAuth2 2 - legged flows .
*
* It primary supports
* - service account authorization
* - authorization where a user already has an access token
*/
class OAuth2 implements \Google\Site_Kit_Dependencies\Google\Auth\FetchAuthTokenInterface
{
const DEFAULT_EXPIRY_SECONDS = 3600 ;
// 1 hour
const DEFAULT_SKEW_SECONDS = 60 ;
// 1 minute
const JWT_URN = 'urn:ietf:params:oauth:grant-type:jwt-bearer' ;
2025-04-25 12:30:07 +02:00
const STS_URN = 'urn:ietf:params:oauth:grant-type:token-exchange' ;
private const STS_REQUESTED_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:access_token' ;
2022-12-15 17:34:08 +01:00
/**
* TODO : determine known methods from the keys of JWT :: methods .
2025-02-28 08:42:11 +01:00
*
* @ var array < string >
2022-12-15 17:34:08 +01:00
*/
2025-02-28 08:42:11 +01:00
public static $knownSigningAlgorithms = [ 'HS256' , 'HS512' , 'HS384' , 'RS256' ];
2022-12-15 17:34:08 +01:00
/**
* The well known grant types .
*
2025-02-28 08:42:11 +01:00
* @ var array < string >
2022-12-15 17:34:08 +01:00
*/
2025-02-28 08:42:11 +01:00
public static $knownGrantTypes = [ 'authorization_code' , 'refresh_token' , 'password' , 'client_credentials' ];
2022-12-15 17:34:08 +01:00
/**
* - authorizationUri
* The authorization server ' s HTTP endpoint capable of
* authenticating the end - user and obtaining authorization .
*
2025-02-28 08:42:11 +01:00
* @ var ? UriInterface
2022-12-15 17:34:08 +01:00
*/
private $authorizationUri ;
/**
* - tokenCredentialUri
* The authorization server ' s HTTP endpoint capable of issuing
* tokens and refreshing expired tokens .
*
* @ var UriInterface
*/
private $tokenCredentialUri ;
/**
* The redirection URI used in the initial request .
*
2025-02-28 08:42:11 +01:00
* @ var ? string
2022-12-15 17:34:08 +01:00
*/
private $redirectUri ;
/**
* A unique identifier issued to the client to identify itself to the
* authorization server .
*
* @ var string
*/
private $clientId ;
/**
* A shared symmetric secret issued by the authorization server , which is
* used to authenticate the client .
*
* @ var string
*/
private $clientSecret ;
/**
* The resource owner ' s username .
*
2025-02-28 08:42:11 +01:00
* @ var ? string
2022-12-15 17:34:08 +01:00
*/
private $username ;
/**
* The resource owner ' s password .
*
2025-02-28 08:42:11 +01:00
* @ var ? string
2022-12-15 17:34:08 +01:00
*/
private $password ;
/**
* The scope of the access request , expressed either as an Array or as a
* space - delimited string .
*
2025-02-28 08:42:11 +01:00
* @ var ? array < string >
2022-12-15 17:34:08 +01:00
*/
private $scope ;
/**
* An arbitrary string designed to allow the client to maintain state .
*
* @ var string
*/
private $state ;
/**
* The authorization code issued to this client .
*
* Only used by the authorization code access grant type .
*
2025-02-28 08:42:11 +01:00
* @ var ? string
2022-12-15 17:34:08 +01:00
*/
private $code ;
/**
* The issuer ID when using assertion profile .
*
2025-02-28 08:42:11 +01:00
* @ var ? string
2022-12-15 17:34:08 +01:00
*/
private $issuer ;
/**
* The target audience for assertions .
*
* @ var string
*/
private $audience ;
/**
* The target sub when issuing assertions .
*
* @ var string
*/
private $sub ;
/**
* The number of seconds assertions are valid for .
*
* @ var int
*/
private $expiry ;
/**
* The signing key when using assertion profile .
*
2025-02-28 08:42:11 +01:00
* @ var ? string
2022-12-15 17:34:08 +01:00
*/
private $signingKey ;
/**
* The signing key id when using assertion profile . Param kid in jwt header
*
* @ var string
*/
private $signingKeyId ;
/**
* The signing algorithm when using an assertion profile .
*
2025-02-28 08:42:11 +01:00
* @ var ? string
2022-12-15 17:34:08 +01:00
*/
private $signingAlgorithm ;
/**
* The refresh token associated with the access token to be refreshed .
*
2025-02-28 08:42:11 +01:00
* @ var ? string
2022-12-15 17:34:08 +01:00
*/
private $refreshToken ;
/**
* The current access token .
*
* @ var string
*/
private $accessToken ;
/**
* The current ID token .
*
* @ var string
*/
private $idToken ;
2025-02-28 08:42:11 +01:00
/**
* The scopes granted to the current access token
*
* @ var string
*/
private $grantedScope ;
2022-12-15 17:34:08 +01:00
/**
* The lifetime in seconds of the current access token .
*
2025-02-28 08:42:11 +01:00
* @ var ? int
2022-12-15 17:34:08 +01:00
*/
private $expiresIn ;
/**
* The expiration time of the access token as a number of seconds since the
* unix epoch .
*
2025-02-28 08:42:11 +01:00
* @ var ? int
2022-12-15 17:34:08 +01:00
*/
private $expiresAt ;
/**
* The issue time of the access token as a number of seconds since the unix
* epoch .
*
2025-02-28 08:42:11 +01:00
* @ var ? int
2022-12-15 17:34:08 +01:00
*/
private $issuedAt ;
/**
* The current grant type .
*
2025-02-28 08:42:11 +01:00
* @ var ? string
2022-12-15 17:34:08 +01:00
*/
private $grantType ;
/**
* When using an extension grant type , this is the set of parameters used by
* that extension .
2025-02-28 08:42:11 +01:00
*
* @ var array < mixed >
2022-12-15 17:34:08 +01:00
*/
private $extensionParams ;
/**
* When using the toJwt function , these claims will be added to the JWT
* payload .
2025-02-28 08:42:11 +01:00
*
* @ var array < mixed >
2022-12-15 17:34:08 +01:00
*/
private $additionalClaims ;
2025-04-25 12:30:07 +02:00
/**
* The code verifier for PKCE for OAuth 2.0 . When set , the authorization
* URI will contain the Code Challenge and Code Challenge Method querystring
* parameters , and the token URI will contain the Code Verifier parameter .
*
* @ see https :// datatracker . ietf . org / doc / html / rfc7636
* @ var ? string
*/
private $codeVerifier ;
/**
* For STS requests .
* A URI that indicates the target service or resource where the client
* intends to use the requested security token .
*/
private ? string $resource ;
/**
* For STS requests .
* A fetcher for the " subject_token " , which is a security token that
* represents the identity of the party on behalf of whom the request is
* being made .
*/
private ? \Google\Site_Kit_Dependencies\Google\Auth\ExternalAccountCredentialSourceInterface $subjectTokenFetcher ;
/**
* For STS requests .
* An identifier , that indicates the type of the security token in the
* subjectToken parameter .
*/
private ? string $subjectTokenType ;
/**
* For STS requests .
* A security token that represents the identity of the acting party .
*/
private ? string $actorToken ;
/**
* For STS requests .
* An identifier that indicates the type of the security token in the
* actorToken parameter .
*/
private ? string $actorTokenType ;
/**
* From STS response .
* An identifier for the representation of the issued security token .
*/
private ? string $issuedTokenType = null ;
/**
* From STS response .
* An identifier for the representation of the issued security token .
*
* @ var array < mixed >
*/
private array $additionalOptions ;
2022-12-15 17:34:08 +01:00
/**
* Create a new OAuthCredentials .
*
* The configuration array accepts various options
*
* - authorizationUri
* The authorization server ' s HTTP endpoint capable of
* authenticating the end - user and obtaining authorization .
*
* - tokenCredentialUri
* The authorization server ' s HTTP endpoint capable of issuing
* tokens and refreshing expired tokens .
*
* - clientId
* A unique identifier issued to the client to identify itself to the
* authorization server .
*
* - clientSecret
* A shared symmetric secret issued by the authorization server ,
* which is used to authenticate the client .
*
* - scope
* The scope of the access request , expressed either as an Array
* or as a space - delimited String .
*
* - state
* An arbitrary string designed to allow the client to maintain state .
*
* - redirectUri
* The redirection URI used in the initial request .
*
* - username
* The resource owner ' s username .
*
* - password
* The resource owner ' s password .
*
* - issuer
* Issuer ID when using assertion profile
*
* - audience
* Target audience for assertions
*
* - expiry
* Number of seconds assertions are valid for
*
* - signingKey
* Signing key when using assertion profile
*
* - signingKeyId
* Signing key id when using assertion profile
*
* - refreshToken
* The refresh token associated with the access token
* to be refreshed .
*
* - accessToken
* The current access token for this client .
*
* - idToken
* The current ID token for this client .
*
* - extensionParams
* When using an extension grant type , this is the set of parameters used
* by that extension .
*
2025-04-25 12:30:07 +02:00
* - codeVerifier
* The code verifier for PKCE for OAuth 2.0 .
*
* - resource
* The target service or resource where the client ntends to use the
* requested security token .
*
* - subjectTokenFetcher
* A fetcher for the " subject_token " , which is a security token that
* represents the identity of the party on behalf of whom the request is
* being made .
*
* - subjectTokenType
* An identifier that indicates the type of the security token in the
* subjectToken parameter .
*
* - actorToken
* A security token that represents the identity of the acting party .
*
* - actorTokenType
* An identifier for the representation of the issued security token .
*
2025-02-28 08:42:11 +01:00
* @ param array < mixed > $config Configuration array
2022-12-15 17:34:08 +01:00
*/
public function __construct ( array $config )
{
2025-04-25 12:30:07 +02:00
$opts = \array_merge ([ 'expiry' => self :: DEFAULT_EXPIRY_SECONDS , 'extensionParams' => [], 'authorizationUri' => null , 'redirectUri' => null , 'tokenCredentialUri' => null , 'state' => null , 'username' => null , 'password' => null , 'clientId' => null , 'clientSecret' => null , 'issuer' => null , 'sub' => null , 'audience' => null , 'signingKey' => null , 'signingKeyId' => null , 'signingAlgorithm' => null , 'scope' => null , 'additionalClaims' => [], 'codeVerifier' => null , 'resource' => null , 'subjectTokenFetcher' => null , 'subjectTokenType' => null , 'actorToken' => null , 'actorTokenType' => null , 'additionalOptions' => []], $config );
2022-12-15 17:34:08 +01:00
$this -> setAuthorizationUri ( $opts [ 'authorizationUri' ]);
$this -> setRedirectUri ( $opts [ 'redirectUri' ]);
$this -> setTokenCredentialUri ( $opts [ 'tokenCredentialUri' ]);
$this -> setState ( $opts [ 'state' ]);
$this -> setUsername ( $opts [ 'username' ]);
$this -> setPassword ( $opts [ 'password' ]);
$this -> setClientId ( $opts [ 'clientId' ]);
$this -> setClientSecret ( $opts [ 'clientSecret' ]);
$this -> setIssuer ( $opts [ 'issuer' ]);
$this -> setSub ( $opts [ 'sub' ]);
$this -> setExpiry ( $opts [ 'expiry' ]);
$this -> setAudience ( $opts [ 'audience' ]);
$this -> setSigningKey ( $opts [ 'signingKey' ]);
$this -> setSigningKeyId ( $opts [ 'signingKeyId' ]);
$this -> setSigningAlgorithm ( $opts [ 'signingAlgorithm' ]);
$this -> setScope ( $opts [ 'scope' ]);
$this -> setExtensionParams ( $opts [ 'extensionParams' ]);
$this -> setAdditionalClaims ( $opts [ 'additionalClaims' ]);
2025-04-25 12:30:07 +02:00
$this -> setCodeVerifier ( $opts [ 'codeVerifier' ]);
// for STS
$this -> resource = $opts [ 'resource' ];
$this -> subjectTokenFetcher = $opts [ 'subjectTokenFetcher' ];
$this -> subjectTokenType = $opts [ 'subjectTokenType' ];
$this -> actorToken = $opts [ 'actorToken' ];
$this -> actorTokenType = $opts [ 'actorTokenType' ];
$this -> additionalOptions = $opts [ 'additionalOptions' ];
2022-12-15 17:34:08 +01:00
$this -> updateToken ( $opts );
}
/**
* Verifies the idToken if present .
*
* - if none is present , return null
* - if present , but invalid , raises DomainException .
* - otherwise returns the payload in the idtoken as a PHP object .
*
* The behavior of this method varies depending on the version of
2025-02-28 08:42:11 +01:00
* `firebase/php-jwt` you are using . In versions 6.0 and above , you cannot
* provide multiple $allowed_algs , and instead must provide an array of Key
* objects as the $publicKey .
2022-12-15 17:34:08 +01:00
*
2025-02-28 08:42:11 +01:00
* @ param string | Key | Key [] $publicKey The public key to use to authenticate the token
* @ param string | array < string > $allowed_algs algorithm or array of supported verification algorithms .
* Providing more than one algorithm will throw an exception .
2022-12-15 17:34:08 +01:00
* @ throws \DomainException if the token is missing an audience .
* @ throws \DomainException if the audience does not match the one set in
* the OAuth2 class instance .
* @ throws \UnexpectedValueException If the token is invalid
2025-02-28 08:42:11 +01:00
* @ throws \InvalidArgumentException If more than one value for allowed_algs is supplied
* @ throws \Firebase\JWT\SignatureInvalidException If the signature is invalid .
* @ throws \Firebase\JWT\BeforeValidException If the token is not yet valid .
* @ throws \Firebase\JWT\ExpiredException If the token has expired .
2022-12-15 17:34:08 +01:00
* @ return null | object
*/
2025-02-28 08:42:11 +01:00
public function verifyIdToken ( $publicKey = null , $allowed_algs = [])
2022-12-15 17:34:08 +01:00
{
$idToken = $this -> getIdToken ();
if ( \is_null ( $idToken )) {
return null ;
}
$resp = $this -> jwtDecode ( $idToken , $publicKey , $allowed_algs );
if ( ! \property_exists ( $resp , 'aud' )) {
throw new \DomainException ( 'No audience found the id token' );
}
if ( $resp -> aud != $this -> getAudience ()) {
throw new \DomainException ( 'Wrong audience present in the id token' );
}
return $resp ;
}
/**
* Obtains the encoded jwt from the instance data .
*
2025-02-28 08:42:11 +01:00
* @ param array < mixed > $config array optional configuration parameters
2022-12-15 17:34:08 +01:00
* @ return string
*/
public function toJwt ( array $config = [])
{
if ( \is_null ( $this -> getSigningKey ())) {
throw new \DomainException ( 'No signing key available' );
}
if ( \is_null ( $this -> getSigningAlgorithm ())) {
throw new \DomainException ( 'No signing algorithm specified' );
}
$now = \time ();
$opts = \array_merge ([ 'skew' => self :: DEFAULT_SKEW_SECONDS ], $config );
$assertion = [ 'iss' => $this -> getIssuer (), 'exp' => $now + $this -> getExpiry (), 'iat' => $now - $opts [ 'skew' ]];
foreach ( $assertion as $k => $v ) {
if ( \is_null ( $v )) {
throw new \DomainException ( $k . ' should not be null' );
}
}
if ( ! \is_null ( $this -> getAudience ())) {
$assertion [ 'aud' ] = $this -> getAudience ();
}
if ( ! \is_null ( $this -> getScope ())) {
$assertion [ 'scope' ] = $this -> getScope ();
}
if ( empty ( $assertion [ 'scope' ]) && empty ( $assertion [ 'aud' ])) {
throw new \DomainException ( 'one of scope or aud should not be null' );
}
if ( ! \is_null ( $this -> getSub ())) {
$assertion [ 'sub' ] = $this -> getSub ();
}
$assertion += $this -> getAdditionalClaims ();
2025-02-28 08:42:11 +01:00
return \Google\Site_Kit_Dependencies\Firebase\JWT\JWT :: encode ( $assertion , $this -> getSigningKey (), $this -> getSigningAlgorithm (), $this -> getSigningKeyId ());
2022-12-15 17:34:08 +01:00
}
/**
* Generates a request for token credentials .
*
2025-04-25 12:30:07 +02:00
* @ param callable $httpHandler callback which delivers psr7 request
2022-12-15 17:34:08 +01:00
* @ return RequestInterface the authorization Url .
*/
2025-04-25 12:30:07 +02:00
public function generateCredentialsRequest ( ? callable $httpHandler = null )
2022-12-15 17:34:08 +01:00
{
$uri = $this -> getTokenCredentialUri ();
if ( \is_null ( $uri )) {
throw new \DomainException ( 'No token credential URI was set.' );
}
$grantType = $this -> getGrantType ();
2025-02-28 08:42:11 +01:00
$params = [ 'grant_type' => $grantType ];
2022-12-15 17:34:08 +01:00
switch ( $grantType ) {
case 'authorization_code' :
$params [ 'code' ] = $this -> getCode ();
$params [ 'redirect_uri' ] = $this -> getRedirectUri ();
2025-04-25 12:30:07 +02:00
if ( $this -> codeVerifier ) {
$params [ 'code_verifier' ] = $this -> codeVerifier ;
}
2022-12-15 17:34:08 +01:00
$this -> addClientCredentials ( $params );
break ;
case 'password' :
$params [ 'username' ] = $this -> getUsername ();
$params [ 'password' ] = $this -> getPassword ();
$this -> addClientCredentials ( $params );
break ;
case 'refresh_token' :
$params [ 'refresh_token' ] = $this -> getRefreshToken ();
$this -> addClientCredentials ( $params );
break ;
case self :: JWT_URN :
$params [ 'assertion' ] = $this -> toJwt ();
break ;
2025-04-25 12:30:07 +02:00
case self :: STS_URN :
$token = $this -> subjectTokenFetcher -> fetchSubjectToken ( $httpHandler );
$params [ 'subject_token' ] = $token ;
$params [ 'subject_token_type' ] = $this -> subjectTokenType ;
$params += \array_filter ([ 'resource' => $this -> resource , 'audience' => $this -> audience , 'scope' => $this -> getScope (), 'requested_token_type' => self :: STS_REQUESTED_TOKEN_TYPE , 'actor_token' => $this -> actorToken , 'actor_token_type' => $this -> actorTokenType ]);
if ( $this -> additionalOptions ) {
$params [ 'options' ] = \json_encode ( $this -> additionalOptions );
}
break ;
2022-12-15 17:34:08 +01:00
default :
if ( ! \is_null ( $this -> getRedirectUri ())) {
# Grant type was supposed to be 'authorization_code', as there
# is a redirect URI.
throw new \DomainException ( 'Missing authorization code' );
}
unset ( $params [ 'grant_type' ]);
if ( ! \is_null ( $grantType )) {
$params [ 'grant_type' ] = $grantType ;
}
$params = \array_merge ( $params , $this -> getExtensionParams ());
}
$headers = [ 'Cache-Control' => 'no-store' , 'Content-Type' => 'application/x-www-form-urlencoded' ];
return new \Google\Site_Kit_Dependencies\GuzzleHttp\Psr7\Request ( 'POST' , $uri , $headers , \Google\Site_Kit_Dependencies\GuzzleHttp\Psr7\Query :: build ( $params ));
}
/**
* Fetches the auth tokens based on the current state .
*
* @ param callable $httpHandler callback which delivers psr7 request
2025-02-28 08:42:11 +01:00
* @ return array < mixed > the response
2022-12-15 17:34:08 +01:00
*/
2025-04-25 12:30:07 +02:00
public function fetchAuthToken ( ? callable $httpHandler = null )
2022-12-15 17:34:08 +01:00
{
if ( \is_null ( $httpHandler )) {
$httpHandler = \Google\Site_Kit_Dependencies\Google\Auth\HttpHandler\HttpHandlerFactory :: build ( \Google\Site_Kit_Dependencies\Google\Auth\HttpHandler\HttpClientCache :: getHttpClient ());
}
2025-04-25 12:30:07 +02:00
$response = $httpHandler ( $this -> generateCredentialsRequest ( $httpHandler ));
2022-12-15 17:34:08 +01:00
$credentials = $this -> parseTokenResponse ( $response );
$this -> updateToken ( $credentials );
2025-02-28 08:42:11 +01:00
if ( isset ( $credentials [ 'scope' ])) {
$this -> setGrantedScope ( $credentials [ 'scope' ]);
}
2022-12-15 17:34:08 +01:00
return $credentials ;
}
/**
* Obtains a key that can used to cache the results of #fetchAuthToken.
*
* The key is derived from the scopes .
*
2025-02-28 08:42:11 +01:00
* @ return ? string a key that may be used to cache the auth token .
2022-12-15 17:34:08 +01:00
*/
public function getCacheKey ()
{
if ( \is_array ( $this -> scope )) {
return \implode ( ':' , $this -> scope );
}
if ( $this -> audience ) {
return $this -> audience ;
}
// If scope has not set, return null to indicate no caching.
return null ;
}
/**
* Parses the fetched tokens .
*
* @ param ResponseInterface $resp the response .
2025-02-28 08:42:11 +01:00
* @ return array < mixed > the tokens parsed from the response body .
2022-12-15 17:34:08 +01:00
* @ throws \Exception
*/
public function parseTokenResponse ( \Google\Site_Kit_Dependencies\Psr\Http\Message\ResponseInterface $resp )
{
$body = ( string ) $resp -> getBody ();
if ( $resp -> hasHeader ( 'Content-Type' ) && $resp -> getHeaderLine ( 'Content-Type' ) == 'application/x-www-form-urlencoded' ) {
2025-02-28 08:42:11 +01:00
$res = [];
2022-12-15 17:34:08 +01:00
\parse_str ( $body , $res );
return $res ;
}
// Assume it's JSON; if it's not throw an exception
if ( null === ( $res = \json_decode ( $body , \true ))) {
throw new \Exception ( 'Invalid JSON response' );
}
return $res ;
}
/**
* Updates an OAuth 2.0 client .
*
* Example :
* `` `
* $oauth -> updateToken ([
* 'refresh_token' => 'n4E9O119d' ,
* 'access_token' => 'FJQbwq9' ,
* 'expires_in' => 3600
* ]);
* `` `
*
2025-02-28 08:42:11 +01:00
* @ param array < mixed > $config
2022-12-15 17:34:08 +01:00
* The configuration parameters related to the token .
*
* - refresh_token
* The refresh token associated with the access token
* to be refreshed .
*
* - access_token
* The current access token for this client .
*
* - id_token
* The current ID token for this client .
*
* - expires_in
* The time in seconds until access token expiration .
*
* - expires_at
* The time as an integer number of seconds since the Epoch
*
* - issued_at
* The timestamp that the token was issued at .
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function updateToken ( array $config )
{
2025-02-28 08:42:11 +01:00
$opts = \array_merge ([ 'extensionParams' => [], 'access_token' => null , 'id_token' => null , 'expires_in' => null , 'expires_at' => null , 'issued_at' => null , 'scope' => null ], $config );
2022-12-15 17:34:08 +01:00
$this -> setExpiresAt ( $opts [ 'expires_at' ]);
$this -> setExpiresIn ( $opts [ 'expires_in' ]);
// By default, the token is issued at `Time.now` when `expiresIn` is set,
// but this can be used to supply a more precise time.
if ( ! \is_null ( $opts [ 'issued_at' ])) {
$this -> setIssuedAt ( $opts [ 'issued_at' ]);
}
$this -> setAccessToken ( $opts [ 'access_token' ]);
$this -> setIdToken ( $opts [ 'id_token' ]);
// The refresh token should only be updated if a value is explicitly
// passed in, as some access token responses do not include a refresh
// token.
if ( \array_key_exists ( 'refresh_token' , $opts )) {
$this -> setRefreshToken ( $opts [ 'refresh_token' ]);
}
2025-04-25 12:30:07 +02:00
// Required for STS response. An identifier for the representation of
// the issued security token.
if ( \array_key_exists ( 'issued_token_type' , $opts )) {
$this -> issuedTokenType = $opts [ 'issued_token_type' ];
}
2022-12-15 17:34:08 +01:00
}
/**
* Builds the authorization Uri that the user should be redirected to .
*
2025-04-25 12:30:07 +02:00
* @ param array < mixed > $config configuration options that customize the return url .
2022-12-15 17:34:08 +01:00
* @ return UriInterface the authorization Url .
* @ throws InvalidArgumentException
*/
public function buildFullAuthorizationUri ( array $config = [])
{
if ( \is_null ( $this -> getAuthorizationUri ())) {
throw new \InvalidArgumentException ( 'requires an authorizationUri to have been set' );
}
$params = \array_merge ([ 'response_type' => 'code' , 'access_type' => 'offline' , 'client_id' => $this -> clientId , 'redirect_uri' => $this -> redirectUri , 'state' => $this -> state , 'scope' => $this -> getScope ()], $config );
// Validate the auth_params
if ( \is_null ( $params [ 'client_id' ])) {
throw new \InvalidArgumentException ( 'missing the required client identifier' );
}
if ( \is_null ( $params [ 'redirect_uri' ])) {
throw new \InvalidArgumentException ( 'missing the required redirect URI' );
}
if ( ! empty ( $params [ 'prompt' ]) && ! empty ( $params [ 'approval_prompt' ])) {
throw new \InvalidArgumentException ( 'prompt and approval_prompt are mutually exclusive' );
}
2025-04-25 12:30:07 +02:00
if ( $this -> codeVerifier ) {
$params [ 'code_challenge' ] = $this -> getCodeChallenge ( $this -> codeVerifier );
$params [ 'code_challenge_method' ] = $this -> getCodeChallengeMethod ();
}
2022-12-15 17:34:08 +01:00
// Construct the uri object; return it if it is valid.
$result = clone $this -> authorizationUri ;
$existingParams = \Google\Site_Kit_Dependencies\GuzzleHttp\Psr7\Query :: parse ( $result -> getQuery ());
$result = $result -> withQuery ( \Google\Site_Kit_Dependencies\GuzzleHttp\Psr7\Query :: build ( \array_merge ( $existingParams , $params )));
if ( $result -> getScheme () != 'https' ) {
throw new \InvalidArgumentException ( 'Authorization endpoint must be protected by TLS' );
}
return $result ;
}
2025-04-25 12:30:07 +02:00
/**
* @ return string | null
*/
public function getCodeVerifier () : ? string
{
return $this -> codeVerifier ;
}
/**
* A cryptographically random string that is used to correlate the
* authorization request to the token request .
*
* The code verifier for PKCE for OAuth 2.0 . When set , the authorization
* URI will contain the Code Challenge and Code Challenge Method querystring
* parameters , and the token URI will contain the Code Verifier parameter .
*
* @ see https :// datatracker . ietf . org / doc / html / rfc7636
*
* @ param string | null $codeVerifier
*/
public function setCodeVerifier ( ? string $codeVerifier ) : void
{
$this -> codeVerifier = $codeVerifier ;
}
/**
* Generates a random 128 - character string for the " code_verifier " parameter
* in PKCE for OAuth 2.0 . This is a cryptographically random string that is
* determined using random_int , hashed using " hash " and sha256 , and base64
* encoded .
*
* When this method is called , the code verifier is set on the object .
*
* @ return string
*/
public function generateCodeVerifier () : string
{
return $this -> codeVerifier = $this -> generateRandomString ( 128 );
}
private function getCodeChallenge ( string $randomString ) : string
{
return \rtrim ( \strtr ( \base64_encode ( \hash ( 'sha256' , $randomString , \true )), '+/' , '-_' ), '=' );
}
private function getCodeChallengeMethod () : string
{
return 'S256' ;
}
private function generateRandomString ( int $length ) : string
{
$validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~' ;
$validCharsLen = \strlen ( $validChars );
$str = '' ;
$i = 0 ;
while ( $i ++ < $length ) {
$str .= $validChars [ \random_int ( 0 , $validCharsLen - 1 )];
}
return $str ;
}
2022-12-15 17:34:08 +01:00
/**
* Sets the authorization server ' s HTTP endpoint capable of authenticating
* the end - user and obtaining authorization .
*
* @ param string $uri
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setAuthorizationUri ( $uri )
{
$this -> authorizationUri = $this -> coerceUri ( $uri );
}
/**
* Gets the authorization server ' s HTTP endpoint capable of authenticating
* the end - user and obtaining authorization .
*
2025-02-28 08:42:11 +01:00
* @ return ? UriInterface
2022-12-15 17:34:08 +01:00
*/
public function getAuthorizationUri ()
{
return $this -> authorizationUri ;
}
/**
* Gets the authorization server ' s HTTP endpoint capable of issuing tokens
* and refreshing expired tokens .
*
2025-02-28 08:42:11 +01:00
* @ return ? UriInterface
2022-12-15 17:34:08 +01:00
*/
public function getTokenCredentialUri ()
{
return $this -> tokenCredentialUri ;
}
/**
* Sets the authorization server ' s HTTP endpoint capable of issuing tokens
* and refreshing expired tokens .
*
* @ param string $uri
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setTokenCredentialUri ( $uri )
{
$this -> tokenCredentialUri = $this -> coerceUri ( $uri );
}
/**
* Gets the redirection URI used in the initial request .
*
2025-02-28 08:42:11 +01:00
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getRedirectUri ()
{
return $this -> redirectUri ;
}
/**
* Sets the redirection URI used in the initial request .
*
2025-02-28 08:42:11 +01:00
* @ param ? string $uri
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setRedirectUri ( $uri )
{
if ( \is_null ( $uri )) {
$this -> redirectUri = null ;
return ;
}
// redirect URI must be absolute
if ( ! $this -> isAbsoluteUri ( $uri )) {
// "postmessage" is a reserved URI string in Google-land
// @see https://developers.google.com/identity/sign-in/web/server-side-flow
if ( 'postmessage' !== ( string ) $uri ) {
throw new \InvalidArgumentException ( 'Redirect URI must be absolute' );
}
}
$this -> redirectUri = ( string ) $uri ;
}
/**
* Gets the scope of the access requests as a space - delimited String .
*
2025-02-28 08:42:11 +01:00
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getScope ()
{
if ( \is_null ( $this -> scope )) {
return $this -> scope ;
}
return \implode ( ' ' , $this -> scope );
}
/**
* Sets the scope of the access request , expressed either as an Array or as
* a space - delimited String .
*
2025-02-28 08:42:11 +01:00
* @ param string | array < string >| null $scope
* @ return void
2022-12-15 17:34:08 +01:00
* @ throws InvalidArgumentException
*/
public function setScope ( $scope )
{
if ( \is_null ( $scope )) {
$this -> scope = null ;
} elseif ( \is_string ( $scope )) {
$this -> scope = \explode ( ' ' , $scope );
} elseif ( \is_array ( $scope )) {
foreach ( $scope as $s ) {
$pos = \strpos ( $s , ' ' );
if ( $pos !== \false ) {
throw new \InvalidArgumentException ( 'array scope values should not contain spaces' );
}
}
$this -> scope = $scope ;
} else {
throw new \InvalidArgumentException ( 'scopes should be a string or array of strings' );
}
}
/**
* Gets the current grant type .
*
2025-02-28 08:42:11 +01:00
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getGrantType ()
{
if ( ! \is_null ( $this -> grantType )) {
return $this -> grantType ;
}
// Returns the inferred grant type, based on the current object instance
// state.
if ( ! \is_null ( $this -> code )) {
return 'authorization_code' ;
}
if ( ! \is_null ( $this -> refreshToken )) {
return 'refresh_token' ;
}
if ( ! \is_null ( $this -> username ) && ! \is_null ( $this -> password )) {
return 'password' ;
}
if ( ! \is_null ( $this -> issuer ) && ! \is_null ( $this -> signingKey )) {
return self :: JWT_URN ;
}
2025-04-25 12:30:07 +02:00
if ( ! \is_null ( $this -> subjectTokenFetcher ) && ! \is_null ( $this -> subjectTokenType )) {
return self :: STS_URN ;
}
2022-12-15 17:34:08 +01:00
return null ;
}
/**
* Sets the current grant type .
*
2025-02-28 08:42:11 +01:00
* @ param string $grantType
* @ return void
2022-12-15 17:34:08 +01:00
* @ throws InvalidArgumentException
*/
public function setGrantType ( $grantType )
{
if ( \in_array ( $grantType , self :: $knownGrantTypes )) {
$this -> grantType = $grantType ;
} else {
// validate URI
if ( ! $this -> isAbsoluteUri ( $grantType )) {
throw new \InvalidArgumentException ( 'invalid grant type' );
}
$this -> grantType = ( string ) $grantType ;
}
}
/**
* Gets an arbitrary string designed to allow the client to maintain state .
*
* @ return string
*/
public function getState ()
{
return $this -> state ;
}
/**
* Sets an arbitrary string designed to allow the client to maintain state .
*
* @ param string $state
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setState ( $state )
{
$this -> state = $state ;
}
/**
* Gets the authorization code issued to this client .
2025-02-28 08:42:11 +01:00
*
* @ return string
2022-12-15 17:34:08 +01:00
*/
public function getCode ()
{
return $this -> code ;
}
/**
* Sets the authorization code issued to this client .
*
* @ param string $code
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setCode ( $code )
{
$this -> code = $code ;
}
/**
* Gets the resource owner ' s username .
2025-02-28 08:42:11 +01:00
*
* @ return string
2022-12-15 17:34:08 +01:00
*/
public function getUsername ()
{
return $this -> username ;
}
/**
* Sets the resource owner ' s username .
*
* @ param string $username
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setUsername ( $username )
{
$this -> username = $username ;
}
/**
* Gets the resource owner ' s password .
2025-02-28 08:42:11 +01:00
*
* @ return string
2022-12-15 17:34:08 +01:00
*/
public function getPassword ()
{
return $this -> password ;
}
/**
* Sets the resource owner ' s password .
*
2025-02-28 08:42:11 +01:00
* @ param string $password
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setPassword ( $password )
{
$this -> password = $password ;
}
/**
* Sets a unique identifier issued to the client to identify itself to the
* authorization server .
2025-02-28 08:42:11 +01:00
*
* @ return string
2022-12-15 17:34:08 +01:00
*/
public function getClientId ()
{
return $this -> clientId ;
}
/**
* Sets a unique identifier issued to the client to identify itself to the
* authorization server .
*
2025-02-28 08:42:11 +01:00
* @ param string $clientId
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setClientId ( $clientId )
{
$this -> clientId = $clientId ;
}
/**
* Gets a shared symmetric secret issued by the authorization server , which
* is used to authenticate the client .
2025-02-28 08:42:11 +01:00
*
* @ return string
2022-12-15 17:34:08 +01:00
*/
public function getClientSecret ()
{
return $this -> clientSecret ;
}
/**
* Sets a shared symmetric secret issued by the authorization server , which
* is used to authenticate the client .
*
2025-02-28 08:42:11 +01:00
* @ param string $clientSecret
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setClientSecret ( $clientSecret )
{
$this -> clientSecret = $clientSecret ;
}
/**
* Gets the Issuer ID when using assertion profile .
2025-02-28 08:42:11 +01:00
*
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getIssuer ()
{
return $this -> issuer ;
}
/**
* Sets the Issuer ID when using assertion profile .
*
* @ param string $issuer
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setIssuer ( $issuer )
{
$this -> issuer = $issuer ;
}
/**
* Gets the target sub when issuing assertions .
2025-02-28 08:42:11 +01:00
*
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getSub ()
{
return $this -> sub ;
}
/**
* Sets the target sub when issuing assertions .
*
* @ param string $sub
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setSub ( $sub )
{
$this -> sub = $sub ;
}
/**
* Gets the target audience when issuing assertions .
2025-02-28 08:42:11 +01:00
*
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getAudience ()
{
return $this -> audience ;
}
/**
* Sets the target audience when issuing assertions .
*
* @ param string $audience
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setAudience ( $audience )
{
$this -> audience = $audience ;
}
/**
* Gets the signing key when using an assertion profile .
2025-02-28 08:42:11 +01:00
*
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getSigningKey ()
{
return $this -> signingKey ;
}
/**
* Sets the signing key when using an assertion profile .
*
* @ param string $signingKey
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setSigningKey ( $signingKey )
{
$this -> signingKey = $signingKey ;
}
/**
* Gets the signing key id when using an assertion profile .
*
2025-02-28 08:42:11 +01:00
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getSigningKeyId ()
{
return $this -> signingKeyId ;
}
/**
* Sets the signing key id when using an assertion profile .
*
* @ param string $signingKeyId
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setSigningKeyId ( $signingKeyId )
{
$this -> signingKeyId = $signingKeyId ;
}
/**
* Gets the signing algorithm when using an assertion profile .
*
2025-02-28 08:42:11 +01:00
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getSigningAlgorithm ()
{
return $this -> signingAlgorithm ;
}
/**
* Sets the signing algorithm when using an assertion profile .
*
2025-02-28 08:42:11 +01:00
* @ param ? string $signingAlgorithm
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setSigningAlgorithm ( $signingAlgorithm )
{
if ( \is_null ( $signingAlgorithm )) {
$this -> signingAlgorithm = null ;
} elseif ( ! \in_array ( $signingAlgorithm , self :: $knownSigningAlgorithms )) {
throw new \InvalidArgumentException ( 'unknown signing algorithm' );
} else {
$this -> signingAlgorithm = $signingAlgorithm ;
}
}
/**
* Gets the set of parameters used by extension when using an extension
* grant type .
2025-02-28 08:42:11 +01:00
*
* @ return array < mixed >
2022-12-15 17:34:08 +01:00
*/
public function getExtensionParams ()
{
return $this -> extensionParams ;
}
/**
* Sets the set of parameters used by extension when using an extension
* grant type .
*
2025-02-28 08:42:11 +01:00
* @ param array < mixed > $extensionParams
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setExtensionParams ( $extensionParams )
{
$this -> extensionParams = $extensionParams ;
}
/**
* Gets the number of seconds assertions are valid for .
2025-02-28 08:42:11 +01:00
*
* @ return int
2022-12-15 17:34:08 +01:00
*/
public function getExpiry ()
{
return $this -> expiry ;
}
/**
* Sets the number of seconds assertions are valid for .
*
* @ param int $expiry
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setExpiry ( $expiry )
{
$this -> expiry = $expiry ;
}
/**
* Gets the lifetime of the access token in seconds .
2025-02-28 08:42:11 +01:00
*
* @ return int
2022-12-15 17:34:08 +01:00
*/
public function getExpiresIn ()
{
return $this -> expiresIn ;
}
/**
* Sets the lifetime of the access token in seconds .
*
2025-02-28 08:42:11 +01:00
* @ param ? int $expiresIn
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setExpiresIn ( $expiresIn )
{
if ( \is_null ( $expiresIn )) {
$this -> expiresIn = null ;
$this -> issuedAt = null ;
} else {
$this -> issuedAt = \time ();
$this -> expiresIn = ( int ) $expiresIn ;
}
}
/**
* Gets the time the current access token expires at .
*
2025-02-28 08:42:11 +01:00
* @ return ? int
2022-12-15 17:34:08 +01:00
*/
public function getExpiresAt ()
{
if ( ! \is_null ( $this -> expiresAt )) {
return $this -> expiresAt ;
}
if ( ! \is_null ( $this -> issuedAt ) && ! \is_null ( $this -> expiresIn )) {
return $this -> issuedAt + $this -> expiresIn ;
}
return null ;
}
/**
* Returns true if the acccess token has expired .
*
* @ return bool
*/
public function isExpired ()
{
$expiration = $this -> getExpiresAt ();
$now = \time ();
return ! \is_null ( $expiration ) && $now >= $expiration ;
}
/**
* Sets the time the current access token expires at .
*
* @ param int $expiresAt
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setExpiresAt ( $expiresAt )
{
$this -> expiresAt = $expiresAt ;
}
/**
* Gets the time the current access token was issued at .
2025-02-28 08:42:11 +01:00
*
* @ return ? int
2022-12-15 17:34:08 +01:00
*/
public function getIssuedAt ()
{
return $this -> issuedAt ;
}
/**
* Sets the time the current access token was issued at .
*
* @ param int $issuedAt
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setIssuedAt ( $issuedAt )
{
$this -> issuedAt = $issuedAt ;
}
/**
* Gets the current access token .
2025-02-28 08:42:11 +01:00
*
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getAccessToken ()
{
return $this -> accessToken ;
}
/**
* Sets the current access token .
*
* @ param string $accessToken
2025-02-28 08:42:11 +01:00
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setAccessToken ( $accessToken )
{
$this -> accessToken = $accessToken ;
}
/**
* Gets the current ID token .
2025-02-28 08:42:11 +01:00
*
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getIdToken ()
{
return $this -> idToken ;
}
/**
* Sets the current ID token .
*
2025-02-28 08:42:11 +01:00
* @ param string $idToken
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setIdToken ( $idToken )
{
$this -> idToken = $idToken ;
}
2025-02-28 08:42:11 +01:00
/**
2025-04-25 12:30:07 +02:00
* Get the granted space - separated scopes ( if they exist ) for the last
* fetched token .
2025-02-28 08:42:11 +01:00
*
* @ return string | null
*/
public function getGrantedScope ()
{
return $this -> grantedScope ;
}
/**
* Sets the current ID token .
*
* @ param string $grantedScope
* @ return void
*/
public function setGrantedScope ( $grantedScope )
{
$this -> grantedScope = $grantedScope ;
}
2022-12-15 17:34:08 +01:00
/**
* Gets the refresh token associated with the current access token .
2025-02-28 08:42:11 +01:00
*
* @ return ? string
2022-12-15 17:34:08 +01:00
*/
public function getRefreshToken ()
{
return $this -> refreshToken ;
}
/**
* Sets the refresh token associated with the current access token .
*
2025-02-28 08:42:11 +01:00
* @ param string $refreshToken
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setRefreshToken ( $refreshToken )
{
$this -> refreshToken = $refreshToken ;
}
/**
* Sets additional claims to be included in the JWT token
*
2025-02-28 08:42:11 +01:00
* @ param array < mixed > $additionalClaims
* @ return void
2022-12-15 17:34:08 +01:00
*/
public function setAdditionalClaims ( array $additionalClaims )
{
$this -> additionalClaims = $additionalClaims ;
}
/**
* Gets the additional claims to be included in the JWT token .
*
2025-02-28 08:42:11 +01:00
* @ return array < mixed >
2022-12-15 17:34:08 +01:00
*/
public function getAdditionalClaims ()
{
return $this -> additionalClaims ;
}
2025-04-25 12:30:07 +02:00
/**
* Gets the additional claims to be included in the JWT token .
*
* @ return ? string
*/
public function getIssuedTokenType ()
{
return $this -> issuedTokenType ;
}
2022-12-15 17:34:08 +01:00
/**
* The expiration of the last received token .
*
2025-02-28 08:42:11 +01:00
* @ return array < mixed >| null
2022-12-15 17:34:08 +01:00
*/
public function getLastReceivedToken ()
{
if ( $token = $this -> getAccessToken ()) {
// the bare necessity of an auth token
$authToken = [ 'access_token' => $token , 'expires_at' => $this -> getExpiresAt ()];
} elseif ( $idToken = $this -> getIdToken ()) {
$authToken = [ 'id_token' => $idToken , 'expires_at' => $this -> getExpiresAt ()];
} else {
return null ;
}
if ( $expiresIn = $this -> getExpiresIn ()) {
$authToken [ 'expires_in' ] = $expiresIn ;
}
if ( $issuedAt = $this -> getIssuedAt ()) {
$authToken [ 'issued_at' ] = $issuedAt ;
}
if ( $refreshToken = $this -> getRefreshToken ()) {
$authToken [ 'refresh_token' ] = $refreshToken ;
}
return $authToken ;
}
/**
* Get the client ID .
*
* Alias of { @ see Google\Auth\OAuth2 :: getClientId ()} .
*
* @ param callable $httpHandler
* @ return string
* @ access private
*/
2025-04-25 12:30:07 +02:00
public function getClientName ( ? callable $httpHandler = null )
2022-12-15 17:34:08 +01:00
{
return $this -> getClientId ();
}
/**
* @ todo handle uri as array
*
2025-02-28 08:42:11 +01:00
* @ param ? string $uri
2022-12-15 17:34:08 +01:00
* @ return null | UriInterface
*/
private function coerceUri ( $uri )
{
if ( \is_null ( $uri )) {
2025-02-28 08:42:11 +01:00
return null ;
2022-12-15 17:34:08 +01:00
}
return \Google\Site_Kit_Dependencies\GuzzleHttp\Psr7\Utils :: uriFor ( $uri );
}
/**
* @ param string $idToken
2025-02-28 08:42:11 +01:00
* @ param Key | Key [] | string | string [] $publicKey
* @ param string | string [] $allowedAlgs
2022-12-15 17:34:08 +01:00
* @ return object
*/
private function jwtDecode ( $idToken , $publicKey , $allowedAlgs )
{
2025-02-28 08:42:11 +01:00
$keys = $this -> getFirebaseJwtKeys ( $publicKey , $allowedAlgs );
// Default exception if none are caught. We are using the same exception
// class and message from firebase/php-jwt to preserve backwards
// compatibility.
$e = new \InvalidArgumentException ( 'Key may not be empty' );
foreach ( $keys as $key ) {
try {
return \Google\Site_Kit_Dependencies\Firebase\JWT\JWT :: decode ( $idToken , $key );
} catch ( \Exception $e ) {
// try next alg
}
2022-12-15 17:34:08 +01:00
}
2025-02-28 08:42:11 +01:00
throw $e ;
2022-12-15 17:34:08 +01:00
}
2025-02-28 08:42:11 +01:00
/**
* @ param Key | Key [] | string | string [] $publicKey
* @ param string | string [] $allowedAlgs
* @ return Key []
*/
private function getFirebaseJwtKeys ( $publicKey , $allowedAlgs )
2022-12-15 17:34:08 +01:00
{
2025-02-28 08:42:11 +01:00
// If $publicKey is instance of Key, return it
if ( $publicKey instanceof \Google\Site_Kit_Dependencies\Firebase\JWT\Key ) {
return [ $publicKey ];
}
// If $allowedAlgs is empty, $publicKey must be Key or Key[].
if ( empty ( $allowedAlgs )) {
$keys = [];
foreach (( array ) $publicKey as $kid => $pubKey ) {
if ( ! $pubKey instanceof \Google\Site_Kit_Dependencies\Firebase\JWT\Key ) {
throw new \InvalidArgumentException ( \sprintf ( 'When allowed algorithms is empty, the public key must' . 'be an instance of %s or an array of %s objects' , \Google\Site_Kit_Dependencies\Firebase\JWT\Key :: class , \Google\Site_Kit_Dependencies\Firebase\JWT\Key :: class ));
}
$keys [ $kid ] = $pubKey ;
}
return $keys ;
}
$allowedAlg = null ;
if ( \is_string ( $allowedAlgs )) {
2025-04-25 12:30:07 +02:00
$allowedAlg = $allowedAlgs ;
2025-02-28 08:42:11 +01:00
} elseif ( \is_array ( $allowedAlgs )) {
if ( \count ( $allowedAlgs ) > 1 ) {
throw new \InvalidArgumentException ( 'To have multiple allowed algorithms, You must provide an' . ' array of Firebase\\JWT\\Key objects.' . ' See https://github.com/firebase/php-jwt for more information.' );
}
$allowedAlg = \array_pop ( $allowedAlgs );
} else {
throw new \InvalidArgumentException ( 'allowed algorithms must be a string or array.' );
}
if ( \is_array ( $publicKey )) {
// When publicKey is greater than 1, create keys with the single alg.
$keys = [];
foreach ( $publicKey as $kid => $pubKey ) {
if ( $pubKey instanceof \Google\Site_Kit_Dependencies\Firebase\JWT\Key ) {
$keys [ $kid ] = $pubKey ;
} else {
$keys [ $kid ] = new \Google\Site_Kit_Dependencies\Firebase\JWT\Key ( $pubKey , $allowedAlg );
}
}
return $keys ;
2022-12-15 17:34:08 +01:00
}
2025-02-28 08:42:11 +01:00
return [ new \Google\Site_Kit_Dependencies\Firebase\JWT\Key ( $publicKey , $allowedAlg )];
2022-12-15 17:34:08 +01:00
}
/**
* Determines if the URI is absolute based on its scheme and host or path
* ( RFC 3986 ) .
*
* @ param string $uri
* @ return bool
*/
private function isAbsoluteUri ( $uri )
{
$uri = $this -> coerceUri ( $uri );
return $uri -> getScheme () && ( $uri -> getHost () || $uri -> getPath ());
}
/**
2025-02-28 08:42:11 +01:00
* @ param array < mixed > $params
* @ return array < mixed >
2022-12-15 17:34:08 +01:00
*/
private function addClientCredentials ( & $params )
{
$clientId = $this -> getClientId ();
$clientSecret = $this -> getClientSecret ();
if ( $clientId && $clientSecret ) {
$params [ 'client_id' ] = $clientId ;
$params [ 'client_secret' ] = $clientSecret ;
}
return $params ;
}
}