From 5c7f2f17f9c471d306955df457c7cab4e5c6ed3b Mon Sep 17 00:00:00 2001 From: Snap Date: Thu, 16 Apr 2015 14:51:26 -0700 Subject: Google's OpenID Connect method $google_client_id & $google_client_secret must be added to db.inc.php! --- .../src/Google/Auth/Abstract.php | 38 ++ .../src/Google/Auth/AppIdentity.php | 121 ++++ .../src/Google/Auth/AssertionCredentials.php | 136 +++++ .../src/Google/Auth/ComputeEngine.php | 146 +++++ .../src/Google/Auth/Exception.php | 24 + .../src/Google/Auth/LoginTicket.php | 71 +++ .../src/Google/Auth/OAuth2.php | 632 +++++++++++++++++++++ .../src/Google/Auth/Simple.php | 64 +++ 8 files changed, 1232 insertions(+) create mode 100644 includes/google-api-php-client-master/src/Google/Auth/Abstract.php create mode 100644 includes/google-api-php-client-master/src/Google/Auth/AppIdentity.php create mode 100644 includes/google-api-php-client-master/src/Google/Auth/AssertionCredentials.php create mode 100644 includes/google-api-php-client-master/src/Google/Auth/ComputeEngine.php create mode 100644 includes/google-api-php-client-master/src/Google/Auth/Exception.php create mode 100644 includes/google-api-php-client-master/src/Google/Auth/LoginTicket.php create mode 100644 includes/google-api-php-client-master/src/Google/Auth/OAuth2.php create mode 100644 includes/google-api-php-client-master/src/Google/Auth/Simple.php (limited to 'includes/google-api-php-client-master/src/Google/Auth') diff --git a/includes/google-api-php-client-master/src/Google/Auth/Abstract.php b/includes/google-api-php-client-master/src/Google/Auth/Abstract.php new file mode 100644 index 0000000..4cd7b55 --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Auth/Abstract.php @@ -0,0 +1,38 @@ + + * + */ +abstract class Google_Auth_Abstract +{ + /** + * An utility function that first calls $this->auth->sign($request) and then + * executes makeRequest() on that signed request. Used for when a request + * should be authenticated + * @param Google_Http_Request $request + * @return Google_Http_Request $request + */ + abstract public function authenticatedRequest(Google_Http_Request $request); + abstract public function sign(Google_Http_Request $request); +} diff --git a/includes/google-api-php-client-master/src/Google/Auth/AppIdentity.php b/includes/google-api-php-client-master/src/Google/Auth/AppIdentity.php new file mode 100644 index 0000000..ba7e720 --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Auth/AppIdentity.php @@ -0,0 +1,121 @@ +client = $client; + } + + /** + * Retrieve an access token for the scopes supplied. + */ + public function authenticateForScope($scopes) + { + if ($this->token && $this->tokenScopes == $scopes) { + return $this->token; + } + + $cacheKey = self::CACHE_PREFIX; + if (is_string($scopes)) { + $cacheKey .= $scopes; + } else if (is_array($scopes)) { + $cacheKey .= implode(":", $scopes); + } + + $this->token = $this->client->getCache()->get($cacheKey); + if (!$this->token) { + $this->retrieveToken($scopes, $cacheKey); + } else if ($this->token['expiration_time'] < time()) { + $this->client->getCache()->delete($cacheKey); + $this->retrieveToken($scopes, $cacheKey); + } + + $this->tokenScopes = $scopes; + return $this->token; + } + + /** + * Retrieve a new access token and store it in cache + * @param mixed $scopes + * @param string $cacheKey + */ + private function retrieveToken($scopes, $cacheKey) + { + $this->token = AppIdentityService::getAccessToken($scopes); + if ($this->token) { + $this->client->getCache()->set( + $cacheKey, + $this->token + ); + } + } + + /** + * Perform an authenticated / signed apiHttpRequest. + * This function takes the apiHttpRequest, calls apiAuth->sign on it + * (which can modify the request in what ever way fits the auth mechanism) + * and then calls apiCurlIO::makeRequest on the signed request + * + * @param Google_Http_Request $request + * @return Google_Http_Request The resulting HTTP response including the + * responseHttpCode, responseHeaders and responseBody. + */ + public function authenticatedRequest(Google_Http_Request $request) + { + $request = $this->sign($request); + return $this->client->getIo()->makeRequest($request); + } + + public function sign(Google_Http_Request $request) + { + if (!$this->token) { + // No token, so nothing to do. + return $request; + } + + $this->client->getLogger()->debug('App Identity authentication'); + + // Add the OAuth2 header to the request + $request->setRequestHeaders( + array('Authorization' => 'Bearer ' . $this->token['access_token']) + ); + + return $request; + } +} diff --git a/includes/google-api-php-client-master/src/Google/Auth/AssertionCredentials.php b/includes/google-api-php-client-master/src/Google/Auth/AssertionCredentials.php new file mode 100644 index 0000000..831d374 --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Auth/AssertionCredentials.php @@ -0,0 +1,136 @@ +serviceAccountName = $serviceAccountName; + $this->scopes = is_string($scopes) ? $scopes : implode(' ', $scopes); + $this->privateKey = $privateKey; + $this->privateKeyPassword = $privateKeyPassword; + $this->assertionType = $assertionType; + $this->sub = $sub; + $this->prn = $sub; + $this->useCache = $useCache; + } + + /** + * Generate a unique key to represent this credential. + * @return string + */ + public function getCacheKey() + { + if (!$this->useCache) { + return false; + } + $h = $this->sub; + $h .= $this->assertionType; + $h .= $this->privateKey; + $h .= $this->scopes; + $h .= $this->serviceAccountName; + return md5($h); + } + + public function generateAssertion() + { + $now = time(); + + $jwtParams = array( + 'aud' => Google_Auth_OAuth2::OAUTH2_TOKEN_URI, + 'scope' => $this->scopes, + 'iat' => $now, + 'exp' => $now + self::MAX_TOKEN_LIFETIME_SECS, + 'iss' => $this->serviceAccountName, + ); + + if ($this->sub !== false) { + $jwtParams['sub'] = $this->sub; + } else if ($this->prn !== false) { + $jwtParams['prn'] = $this->prn; + } + + return $this->makeSignedJwt($jwtParams); + } + + /** + * Creates a signed JWT. + * @param array $payload + * @return string The signed JWT. + */ + private function makeSignedJwt($payload) + { + $header = array('typ' => 'JWT', 'alg' => 'RS256'); + + $payload = json_encode($payload); + // Handle some overzealous escaping in PHP json that seemed to cause some errors + // with claimsets. + $payload = str_replace('\/', '/', $payload); + + $segments = array( + Google_Utils::urlSafeB64Encode(json_encode($header)), + Google_Utils::urlSafeB64Encode($payload) + ); + + $signingInput = implode('.', $segments); + $signer = new Google_Signer_P12($this->privateKey, $this->privateKeyPassword); + $signature = $signer->sign($signingInput); + $segments[] = Google_Utils::urlSafeB64Encode($signature); + + return implode(".", $segments); + } +} diff --git a/includes/google-api-php-client-master/src/Google/Auth/ComputeEngine.php b/includes/google-api-php-client-master/src/Google/Auth/ComputeEngine.php new file mode 100644 index 0000000..88ff6ff --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Auth/ComputeEngine.php @@ -0,0 +1,146 @@ + + */ +class Google_Auth_ComputeEngine extends Google_Auth_Abstract +{ + const METADATA_AUTH_URL = + 'http://metadata/computeMetadata/v1/instance/service-accounts/default/token'; + private $client; + private $token; + + public function __construct(Google_Client $client, $config = null) + { + $this->client = $client; + } + + /** + * Perform an authenticated / signed apiHttpRequest. + * This function takes the apiHttpRequest, calls apiAuth->sign on it + * (which can modify the request in what ever way fits the auth mechanism) + * and then calls apiCurlIO::makeRequest on the signed request + * + * @param Google_Http_Request $request + * @return Google_Http_Request The resulting HTTP response including the + * responseHttpCode, responseHeaders and responseBody. + */ + public function authenticatedRequest(Google_Http_Request $request) + { + $request = $this->sign($request); + return $this->client->getIo()->makeRequest($request); + } + + /** + * @param string $token + * @throws Google_Auth_Exception + */ + public function setAccessToken($token) + { + $token = json_decode($token, true); + if ($token == null) { + throw new Google_Auth_Exception('Could not json decode the token'); + } + if (! isset($token['access_token'])) { + throw new Google_Auth_Exception("Invalid token format"); + } + $token['created'] = time(); + $this->token = $token; + } + + public function getAccessToken() + { + return json_encode($this->token); + } + + /** + * Acquires a new access token from the compute engine metadata server. + * @throws Google_Auth_Exception + */ + public function acquireAccessToken() + { + $request = new Google_Http_Request( + self::METADATA_AUTH_URL, + 'GET', + array( + 'Metadata-Flavor' => 'Google' + ) + ); + $request->disableGzip(); + $response = $this->client->getIo()->makeRequest($request); + + if ($response->getResponseHttpCode() == 200) { + $this->setAccessToken($response->getResponseBody()); + $this->token['created'] = time(); + return $this->getAccessToken(); + } else { + throw new Google_Auth_Exception( + sprintf( + "Error fetching service account access token, message: '%s'", + $response->getResponseBody() + ), + $response->getResponseHttpCode() + ); + } + } + + /** + * Include an accessToken in a given apiHttpRequest. + * @param Google_Http_Request $request + * @return Google_Http_Request + * @throws Google_Auth_Exception + */ + public function sign(Google_Http_Request $request) + { + if ($this->isAccessTokenExpired()) { + $this->acquireAccessToken(); + } + + $this->client->getLogger()->debug('Compute engine service account authentication'); + + $request->setRequestHeaders( + array('Authorization' => 'Bearer ' . $this->token['access_token']) + ); + + return $request; + } + + /** + * Returns if the access_token is expired. + * @return bool Returns True if the access_token is expired. + */ + public function isAccessTokenExpired() + { + if (!$this->token || !isset($this->token['created'])) { + return true; + } + + // If the token is set to expire in the next 30 seconds. + $expired = ($this->token['created'] + + ($this->token['expires_in'] - 30)) < time(); + + return $expired; + } +} diff --git a/includes/google-api-php-client-master/src/Google/Auth/Exception.php b/includes/google-api-php-client-master/src/Google/Auth/Exception.php new file mode 100644 index 0000000..e4b75c1 --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Auth/Exception.php @@ -0,0 +1,24 @@ + + */ +class Google_Auth_LoginTicket +{ + const USER_ATTR = "sub"; + + // Information from id token envelope. + private $envelope; + + // Information from id token payload. + private $payload; + + /** + * Creates a user based on the supplied token. + * + * @param string $envelope Header from a verified authentication token. + * @param string $payload Information from a verified authentication token. + */ + public function __construct($envelope, $payload) + { + $this->envelope = $envelope; + $this->payload = $payload; + } + + /** + * Returns the numeric identifier for the user. + * @throws Google_Auth_Exception + * @return + */ + public function getUserId() + { + if (array_key_exists(self::USER_ATTR, $this->payload)) { + return $this->payload[self::USER_ATTR]; + } + throw new Google_Auth_Exception("No user_id in token"); + } + + /** + * Returns attributes from the login ticket. This can contain + * various information about the user session. + * @return array + */ + public function getAttributes() + { + return array("envelope" => $this->envelope, "payload" => $this->payload); + } +} diff --git a/includes/google-api-php-client-master/src/Google/Auth/OAuth2.php b/includes/google-api-php-client-master/src/Google/Auth/OAuth2.php new file mode 100644 index 0000000..5206e3b --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Auth/OAuth2.php @@ -0,0 +1,632 @@ +client = $client; + } + + /** + * Perform an authenticated / signed apiHttpRequest. + * This function takes the apiHttpRequest, calls apiAuth->sign on it + * (which can modify the request in what ever way fits the auth mechanism) + * and then calls apiCurlIO::makeRequest on the signed request + * + * @param Google_Http_Request $request + * @return Google_Http_Request The resulting HTTP response including the + * responseHttpCode, responseHeaders and responseBody. + */ + public function authenticatedRequest(Google_Http_Request $request) + { + $request = $this->sign($request); + return $this->client->getIo()->makeRequest($request); + } + + /** + * @param string $code + * @throws Google_Auth_Exception + * @return string + */ + public function authenticate($code) + { + if (strlen($code) == 0) { + throw new Google_Auth_Exception("Invalid code"); + } + + // We got here from the redirect from a successful authorization grant, + // fetch the access token + $request = new Google_Http_Request( + self::OAUTH2_TOKEN_URI, + 'POST', + array(), + array( + 'code' => $code, + 'grant_type' => 'authorization_code', + 'redirect_uri' => $this->client->getClassConfig($this, 'redirect_uri'), + 'client_id' => $this->client->getClassConfig($this, 'client_id'), + 'client_secret' => $this->client->getClassConfig($this, 'client_secret') + ) + ); + $request->disableGzip(); + $response = $this->client->getIo()->makeRequest($request); + + if ($response->getResponseHttpCode() == 200) { + $this->setAccessToken($response->getResponseBody()); + $this->token['created'] = time(); + return $this->getAccessToken(); + } else { + $decodedResponse = json_decode($response->getResponseBody(), true); + if ($decodedResponse != null && $decodedResponse['error']) { + $errorText = $decodedResponse['error']; + if (isset($decodedResponse['error_description'])) { + $errorText .= ": " . $decodedResponse['error_description']; + } + } + throw new Google_Auth_Exception( + sprintf( + "Error fetching OAuth2 access token, message: '%s'", + $errorText + ), + $response->getResponseHttpCode() + ); + } + } + + /** + * Create a URL to obtain user authorization. + * The authorization endpoint allows the user to first + * authenticate, and then grant/deny the access request. + * @param string $scope The scope is expressed as a list of space-delimited strings. + * @return string + */ + public function createAuthUrl($scope) + { + $params = array( + 'response_type' => 'code', + 'redirect_uri' => $this->client->getClassConfig($this, 'redirect_uri'), + 'client_id' => $this->client->getClassConfig($this, 'client_id'), + 'scope' => $scope, + 'access_type' => $this->client->getClassConfig($this, 'access_type'), + ); + + // Prefer prompt to approval prompt. + if ($this->client->getClassConfig($this, 'prompt')) { + $params = $this->maybeAddParam($params, 'prompt'); + } else { + $params = $this->maybeAddParam($params, 'approval_prompt'); + } + $params = $this->maybeAddParam($params, 'login_hint'); + $params = $this->maybeAddParam($params, 'hd'); + $params = $this->maybeAddParam($params, 'openid.realm'); + $params = $this->maybeAddParam($params, 'include_granted_scopes'); + + // If the list of scopes contains plus.login, add request_visible_actions + // to auth URL. + $rva = $this->client->getClassConfig($this, 'request_visible_actions'); + if (strpos($scope, 'plus.login') && strlen($rva) > 0) { + $params['request_visible_actions'] = $rva; + } + + if (isset($this->state)) { + $params['state'] = $this->state; + } + + return self::OAUTH2_AUTH_URL . "?" . http_build_query($params, '', '&'); + } + + /** + * @param string $token + * @throws Google_Auth_Exception + */ + public function setAccessToken($token) + { + $token = json_decode($token, true); + if ($token == null) { + throw new Google_Auth_Exception('Could not json decode the token'); + } + if (! isset($token['access_token'])) { + throw new Google_Auth_Exception("Invalid token format"); + } + $this->token = $token; + } + + public function getAccessToken() + { + return json_encode($this->token); + } + + public function getRefreshToken() + { + if (array_key_exists('refresh_token', $this->token)) { + return $this->token['refresh_token']; + } else { + return null; + } + } + + public function setState($state) + { + $this->state = $state; + } + + public function setAssertionCredentials(Google_Auth_AssertionCredentials $creds) + { + $this->assertionCredentials = $creds; + } + + /** + * Include an accessToken in a given apiHttpRequest. + * @param Google_Http_Request $request + * @return Google_Http_Request + * @throws Google_Auth_Exception + */ + public function sign(Google_Http_Request $request) + { + // add the developer key to the request before signing it + if ($this->client->getClassConfig($this, 'developer_key')) { + $request->setQueryParam('key', $this->client->getClassConfig($this, 'developer_key')); + } + + // Cannot sign the request without an OAuth access token. + if (null == $this->token && null == $this->assertionCredentials) { + return $request; + } + + // Check if the token is set to expire in the next 30 seconds + // (or has already expired). + if ($this->isAccessTokenExpired()) { + if ($this->assertionCredentials) { + $this->refreshTokenWithAssertion(); + } else { + $this->client->getLogger()->debug('OAuth2 access token expired'); + if (! array_key_exists('refresh_token', $this->token)) { + $error = "The OAuth 2.0 access token has expired," + ." and a refresh token is not available. Refresh tokens" + ." are not returned for responses that were auto-approved."; + + $this->client->getLogger()->error($error); + throw new Google_Auth_Exception($error); + } + $this->refreshToken($this->token['refresh_token']); + } + } + + $this->client->getLogger()->debug('OAuth2 authentication'); + + // Add the OAuth2 header to the request + $request->setRequestHeaders( + array('Authorization' => 'Bearer ' . $this->token['access_token']) + ); + + return $request; + } + + /** + * Fetches a fresh access token with the given refresh token. + * @param string $refreshToken + * @return void + */ + public function refreshToken($refreshToken) + { + $this->refreshTokenRequest( + array( + 'client_id' => $this->client->getClassConfig($this, 'client_id'), + 'client_secret' => $this->client->getClassConfig($this, 'client_secret'), + 'refresh_token' => $refreshToken, + 'grant_type' => 'refresh_token' + ) + ); + } + + /** + * Fetches a fresh access token with a given assertion token. + * @param Google_Auth_AssertionCredentials $assertionCredentials optional. + * @return void + */ + public function refreshTokenWithAssertion($assertionCredentials = null) + { + if (!$assertionCredentials) { + $assertionCredentials = $this->assertionCredentials; + } + + $cacheKey = $assertionCredentials->getCacheKey(); + + if ($cacheKey) { + // We can check whether we have a token available in the + // cache. If it is expired, we can retrieve a new one from + // the assertion. + $token = $this->client->getCache()->get($cacheKey); + if ($token) { + $this->setAccessToken($token); + } + if (!$this->isAccessTokenExpired()) { + return; + } + } + + $this->client->getLogger()->debug('OAuth2 access token expired'); + $this->refreshTokenRequest( + array( + 'grant_type' => 'assertion', + 'assertion_type' => $assertionCredentials->assertionType, + 'assertion' => $assertionCredentials->generateAssertion(), + ) + ); + + if ($cacheKey) { + // Attempt to cache the token. + $this->client->getCache()->set( + $cacheKey, + $this->getAccessToken() + ); + } + } + + private function refreshTokenRequest($params) + { + if (isset($params['assertion'])) { + $this->client->getLogger()->info( + 'OAuth2 access token refresh with Signed JWT assertion grants.' + ); + } else { + $this->client->getLogger()->info('OAuth2 access token refresh'); + } + + $http = new Google_Http_Request( + self::OAUTH2_TOKEN_URI, + 'POST', + array(), + $params + ); + $http->disableGzip(); + $request = $this->client->getIo()->makeRequest($http); + + $code = $request->getResponseHttpCode(); + $body = $request->getResponseBody(); + if (200 == $code) { + $token = json_decode($body, true); + if ($token == null) { + throw new Google_Auth_Exception("Could not json decode the access token"); + } + + if (! isset($token['access_token']) || ! isset($token['expires_in'])) { + throw new Google_Auth_Exception("Invalid token format"); + } + + if (isset($token['id_token'])) { + $this->token['id_token'] = $token['id_token']; + } + $this->token['access_token'] = $token['access_token']; + $this->token['expires_in'] = $token['expires_in']; + $this->token['created'] = time(); + } else { + throw new Google_Auth_Exception("Error refreshing the OAuth2 token, message: '$body'", $code); + } + } + + /** + * Revoke an OAuth2 access token or refresh token. This method will revoke the current access + * token, if a token isn't provided. + * @throws Google_Auth_Exception + * @param string|null $token The token (access token or a refresh token) that should be revoked. + * @return boolean Returns True if the revocation was successful, otherwise False. + */ + public function revokeToken($token = null) + { + if (!$token) { + if (!$this->token) { + // Not initialized, no token to actually revoke + return false; + } elseif (array_key_exists('refresh_token', $this->token)) { + $token = $this->token['refresh_token']; + } else { + $token = $this->token['access_token']; + } + } + $request = new Google_Http_Request( + self::OAUTH2_REVOKE_URI, + 'POST', + array(), + "token=$token" + ); + $request->disableGzip(); + $response = $this->client->getIo()->makeRequest($request); + $code = $response->getResponseHttpCode(); + if ($code == 200) { + $this->token = null; + return true; + } + + return false; + } + + /** + * Returns if the access_token is expired. + * @return bool Returns True if the access_token is expired. + */ + public function isAccessTokenExpired() + { + if (!$this->token || !isset($this->token['created'])) { + return true; + } + + // If the token is set to expire in the next 30 seconds. + $expired = ($this->token['created'] + + ($this->token['expires_in'] - 30)) < time(); + + return $expired; + } + + // Gets federated sign-on certificates to use for verifying identity tokens. + // Returns certs as array structure, where keys are key ids, and values + // are PEM encoded certificates. + private function getFederatedSignOnCerts() + { + return $this->retrieveCertsFromLocation( + $this->client->getClassConfig($this, 'federated_signon_certs_url') + ); + } + + /** + * Retrieve and cache a certificates file. + * + * @param $url string location + * @throws Google_Auth_Exception + * @return array certificates + */ + public function retrieveCertsFromLocation($url) + { + // If we're retrieving a local file, just grab it. + if ("http" != substr($url, 0, 4)) { + $file = file_get_contents($url); + if ($file) { + return json_decode($file, true); + } else { + throw new Google_Auth_Exception( + "Failed to retrieve verification certificates: '" . + $url . "'." + ); + } + } + + // This relies on makeRequest caching certificate responses. + $request = $this->client->getIo()->makeRequest( + new Google_Http_Request( + $url + ) + ); + if ($request->getResponseHttpCode() == 200) { + $certs = json_decode($request->getResponseBody(), true); + if ($certs) { + return $certs; + } + } + throw new Google_Auth_Exception( + "Failed to retrieve verification certificates: '" . + $request->getResponseBody() . "'.", + $request->getResponseHttpCode() + ); + } + + /** + * Verifies an id token and returns the authenticated apiLoginTicket. + * Throws an exception if the id token is not valid. + * The audience parameter can be used to control which id tokens are + * accepted. By default, the id token must have been issued to this OAuth2 client. + * + * @param $id_token + * @param $audience + * @return Google_Auth_LoginTicket + */ + public function verifyIdToken($id_token = null, $audience = null) + { + if (!$id_token) { + $id_token = $this->token['id_token']; + } + $certs = $this->getFederatedSignonCerts(); + if (!$audience) { + $audience = $this->client->getClassConfig($this, 'client_id'); + } + + return $this->verifySignedJwtWithCerts($id_token, $certs, $audience, self::OAUTH2_ISSUER); + } + + /** + * Verifies the id token, returns the verified token contents. + * + * @param $jwt string the token + * @param $certs array of certificates + * @param $required_audience string the expected consumer of the token + * @param [$issuer] the expected issues, defaults to Google + * @param [$max_expiry] the max lifetime of a token, defaults to MAX_TOKEN_LIFETIME_SECS + * @throws Google_Auth_Exception + * @return mixed token information if valid, false if not + */ + public function verifySignedJwtWithCerts( + $jwt, + $certs, + $required_audience, + $issuer = null, + $max_expiry = null + ) { + if (!$max_expiry) { + // Set the maximum time we will accept a token for. + $max_expiry = self::MAX_TOKEN_LIFETIME_SECS; + } + + $segments = explode(".", $jwt); + if (count($segments) != 3) { + throw new Google_Auth_Exception("Wrong number of segments in token: $jwt"); + } + $signed = $segments[0] . "." . $segments[1]; + $signature = Google_Utils::urlSafeB64Decode($segments[2]); + + // Parse envelope. + $envelope = json_decode(Google_Utils::urlSafeB64Decode($segments[0]), true); + if (!$envelope) { + throw new Google_Auth_Exception("Can't parse token envelope: " . $segments[0]); + } + + // Parse token + $json_body = Google_Utils::urlSafeB64Decode($segments[1]); + $payload = json_decode($json_body, true); + if (!$payload) { + throw new Google_Auth_Exception("Can't parse token payload: " . $segments[1]); + } + + // Check signature + $verified = false; + foreach ($certs as $keyName => $pem) { + $public_key = new Google_Verifier_Pem($pem); + if ($public_key->verify($signed, $signature)) { + $verified = true; + break; + } + } + + if (!$verified) { + throw new Google_Auth_Exception("Invalid token signature: $jwt"); + } + + // Check issued-at timestamp + $iat = 0; + if (array_key_exists("iat", $payload)) { + $iat = $payload["iat"]; + } + if (!$iat) { + throw new Google_Auth_Exception("No issue time in token: $json_body"); + } + $earliest = $iat - self::CLOCK_SKEW_SECS; + + // Check expiration timestamp + $now = time(); + $exp = 0; + if (array_key_exists("exp", $payload)) { + $exp = $payload["exp"]; + } + if (!$exp) { + throw new Google_Auth_Exception("No expiration time in token: $json_body"); + } + if ($exp >= $now + $max_expiry) { + throw new Google_Auth_Exception( + sprintf("Expiration time too far in future: %s", $json_body) + ); + } + + $latest = $exp + self::CLOCK_SKEW_SECS; + if ($now < $earliest) { + throw new Google_Auth_Exception( + sprintf( + "Token used too early, %s < %s: %s", + $now, + $earliest, + $json_body + ) + ); + } + if ($now > $latest) { + throw new Google_Auth_Exception( + sprintf( + "Token used too late, %s > %s: %s", + $now, + $latest, + $json_body + ) + ); + } + + $iss = $payload['iss']; + if ($issuer && $iss != $issuer) { + throw new Google_Auth_Exception( + sprintf( + "Invalid issuer, %s != %s: %s", + $iss, + $issuer, + $json_body + ) + ); + } + + // Check audience + $aud = $payload["aud"]; + if ($aud != $required_audience) { + throw new Google_Auth_Exception( + sprintf( + "Wrong recipient, %s != %s:", + $aud, + $required_audience, + $json_body + ) + ); + } + + // All good. + return new Google_Auth_LoginTicket($envelope, $payload); + } + + /** + * Add a parameter to the auth params if not empty string. + */ + private function maybeAddParam($params, $name) + { + $param = $this->client->getClassConfig($this, $name); + if ($param != '') { + $params[$name] = $param; + } + return $params; + } +} diff --git a/includes/google-api-php-client-master/src/Google/Auth/Simple.php b/includes/google-api-php-client-master/src/Google/Auth/Simple.php new file mode 100644 index 0000000..69476c7 --- /dev/null +++ b/includes/google-api-php-client-master/src/Google/Auth/Simple.php @@ -0,0 +1,64 @@ +client = $client; + } + + /** + * Perform an authenticated / signed apiHttpRequest. + * This function takes the apiHttpRequest, calls apiAuth->sign on it + * (which can modify the request in what ever way fits the auth mechanism) + * and then calls apiCurlIO::makeRequest on the signed request + * + * @param Google_Http_Request $request + * @return Google_Http_Request The resulting HTTP response including the + * responseHttpCode, responseHeaders and responseBody. + */ + public function authenticatedRequest(Google_Http_Request $request) + { + $request = $this->sign($request); + return $this->io->makeRequest($request); + } + + public function sign(Google_Http_Request $request) + { + $key = $this->client->getClassConfig($this, 'developer_key'); + if ($key) { + $this->client->getLogger()->debug( + 'Simple API Access developer key authentication' + ); + $request->setQueryParam('key', $key); + } + return $request; + } +} -- cgit v1.2.3