Клиентская библиотека Google php ломается при аутентификации с ошибкой cURL 6

Я получил google api-php-client и не могу войти в систему, так как всегда получаю сообщение об ошибке:

[02-Nov-2017 15:30:52 Europe/Helsinki] ***START***
scripto\CustomException: [0: E_CUSTOM: Suppressed or Custom error] cURL error 6: Could not resolve host: www.googleapis.com (see http://curl.haxx.se/libcurl/c/libcurl-errors.html).
in /home/suexec-test/server/GuzzleHttp/Handler/CurlFactory.php on line 186
[Trace]:
#0 /home/suexec-test/server/ExceptionFactory.php(117): scripto\CustomException::buildFromException(Object(GuzzleHttp\Exception\ConnectException))
#1 [internal function]: scripto\ExceptionFactory::exception_handler(Object(GuzzleHttp\Exception\ConnectException))
#2 {main}
[Previous]:
scripto\CustomException: [0: E_CUSTOM: Suppressed or Custom error] cURL error 6: Could not resolve host: www.googleapis.com (see http://curl.haxx.se/libcurl/c/libcurl-errors.html).
in /home/suexec-test/server/GuzzleHttp/Handler/CurlFactory.php on line 186
[Trace]:
#0 /home/suexec-test/server/GuzzleHttp/Handler/CurlFactory.php(150): GuzzleHttp\Handler\CurlFactory::createRejection(Object(GuzzleHttp\Handler\EasyHandle), Array)
#1 /home/suexec-test/server/GuzzleHttp/Handler/CurlFactory.php(103): GuzzleHttp\Handler\CurlFactory::finishError(Object(GuzzleHttp\Handler\CurlHandler), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory))
#2 /home/suexec-test/server/GuzzleHttp/Handler/CurlHandler.php(43): GuzzleHttp\Handler\CurlFactory::finish(Object(GuzzleHttp\Handler\CurlHandler), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory))
#3 /home/suexec-test/server/GuzzleHttp/Handler/Proxy.php(28): GuzzleHttp\Handler\CurlHandler->__invoke(Object(GuzzleHttp\Psr7\Request), Array)
#4 /home/suexec-test/server/GuzzleHttp/Handler/Proxy.php(51): GuzzleHttp\Handler\Proxy::GuzzleHttp\Handler\{closure}(Object(GuzzleHttp\Psr7\Request), Array)
#5 /home/suexec-test/server/GuzzleHttp/PrepareBodyMiddleware.php(66): GuzzleHttp\Handler\Proxy::GuzzleHttp\Handler\{closure}(Object(GuzzleHttp\Psr7\Request), Array)
#6 /home/suexec-test/server/GuzzleHttp/Middleware.php(30): GuzzleHttp\PrepareBodyMiddleware->__invoke(Object(GuzzleHttp\Psr7\Request), Array)
#7 /home/suexec-test/server/GuzzleHttp/RedirectMiddleware.php(70): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Request), Array)
#8 /home/suexec-test/server/GuzzleHttp/Middleware.php(57): GuzzleHttp\RedirectMiddleware->__invoke(Object(GuzzleHttp\Psr7\Request), Array)
#9 /home/suexec-test/server/GuzzleHttp/HandlerStack.php(67): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Request), Array)
#10 /home/suexec-test/server/GuzzleHttp/Client.php(281): GuzzleHttp\HandlerStack->__invoke(Object(GuzzleHttp\Psr7\Request), Array)
#11 /home/suexec-test/server/GuzzleHttp/Client.php(103): GuzzleHttp\Client->transfer(Object(GuzzleHttp\Psr7\Request), Array)
#12 /home/suexec-test/server/GuzzleHttp/Client.php(110): GuzzleHttp\Client->sendAsync(Object(GuzzleHttp\Psr7\Request), Array)
#13 /home/suexec-test/server/Google/Auth/HttpHandler/Guzzle6HttpHandler.php(34): GuzzleHttp\Client->send(Object(GuzzleHttp\Psr7\Request), Array)
#14 /home/suexec-test/server/Google/Auth/OAuth2.php(501): Google\Auth\HttpHandler\Guzzle6HttpHandler->__invoke(Object(GuzzleHttp\Psr7\Request))
#15 /home/suexec-test/server/Google/Google_Client.php(195): Google\Auth\OAuth2->fetchAuthToken(Object(Google\Auth\HttpHandler\Guzzle6HttpHandler))
#16 /home/suexec-test/server/Google/Google_Client.php(174): Google_Client->fetchAccessTokenWithAuthCode('4/4BhYCtvi43DGf...')
#17 /home/suexec-test/tests/testGoogle-4.php(56): Google_Client->authenticate('4/4BhYCtvi43DGf...')
#18 /home/suexec-test/server/Dispatcher.php(96): require_once('/home/suexec-te...')
#19 /home/suexec-test/server/Controller.php(68): scripto\Dispatcher->run()
#20 /home/suexec-test/public_html/index.php(7): scripto\Controller->run()
#21 {main}
***END***

Я создал веб- приложение, полномочия

определил URL перенаправления, URL перенаправления

и включен G+, G + сервис

Проблема в том, что даже если все классы загружены, мой код не может пройти $client->authenticate($_GET['code']);,

Я подготовил тестовый файл для вас: в http://localhost/auth/google/login а также http://localhost/auth/google/logout Вы должны вызвать тот же файл, чтобы наблюдать за ошибкой.

Тестовый файл является модификацией этого поста:

<?php
if (\session_status() != PHP_SESSION_ACTIVE) {
   Session_start();
}

$whoami = $_SERVER['REQUEST_URI'];
$login = $logout = false;
if (\strpos($whoami, '/login') !== false)
   $login = true;
if (\strpos($whoami, '/logout') !== false)
   $logout = true;

/*
 * Configuration and setup Google SDK
 */
$appId              = '519650546062-XXXXXX.apps.googleusercontent.com';
$appSecret          = 'XXXXXXX';
// CALLED BY US & GOOGLE!
$loginUri           = 'http://localhost/auth/google/login';
// CALLED BY US!
$logoutUri          = 'http://localhost/auth/google/logout';
$access             = 'online';
$scopes             = "openid profile email";
$incremental_scopes = false;  // what if for true?
//$state = 123;

//Create Google Client
$client = new Google_Client();
$client->setApplicationName("PHP Google OAuth Login Example");
$client->setClientId($appId);
$client->setClientSecret($appSecret);
$client->setRedirectUri($loginUri);
$client->setAccessType($access);
$client->setIncludeGrantedScopes($incremental_scopes);
$client->addScope($scopes);
//$client->setState($state);

//Send Client Request?
$objOAuthService = new Google_Service_Oauth2($client);

//CALLED BY US
if ($logout) {
  unset($_SESSION['access_token']);
  $client->revokeToken();
  // Back to login!
  header('Location: ' . filter_var($loginUri, FILTER_SANITIZE_URL));
}

//CALLED BY GOOGLE
if (isset($_GET['code'])) {
error_log('1....I\'M GOING TO BREAK...');
   $client->authenticate($_GET['code']);
   $_SESSION['access_token'] = $client->getAccessToken();
   // Back to this file!
   header('Location: ' . filter_var($loginUri, FILTER_SANITIZE_URL));
}

//CALLED BY US 2ND TIME (after previous header)
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
error_log('2....session access token='.$_SESSION['access_token']);
   $client->setAccessToken($_SESSION['access_token']);
}

//ONLY DURING 2ND CALL (from previous if...)
if ($client->getAccessToken()) {
   $userData = $objOAuthService->userinfo->get();
   if(!empty($userData)) {
      // do sth with data!
   }
   $_SESSION['access_token'] = $client->getAccessToken();
error_log('3.....user data:'.print_r($userData, true));
} else {
   $authUrl = $client->createAuthUrl();
}

   $out = <<<EOT
<html>
   <head> 
      <title>Google OAuth v.2.0 Login test</title>
   </head>
   <body>

EOT;
if (isset($authUrl)) {
   $d = \urldecode($authUrl);
   $out .= <<<EOT
   <p>decoded authUrl = '{$d}'</p>
EOT;
   }
$out .= <<<EOT
   <h2>PHP Google OAuth 2.0 Login</h2>
EOT;
if (isset($authUrl)) {
   $out .= <<<EOT
      <p><a href='{$authUrl}'>Login with Google API</a></p>
EOT;
} else {
   $out .= <<<EOT
      <p>Welcome <a href="{$userData['link']}">{$userData['name']}</a>.</p>
      <p>Your email: {$userData['email']}</p>
      <p><a href={$logoutUri}>Logout</a></p>

EOT;
}
$e = \nl2br(\htmlspecialchars(\print_r($_SESSION, true)));
$out .= <<<EOT
      <p><h3>SESSION:</h3></p>
      <p>{$e}</p>
   </body>
</html>
EOT;

echo $out;

Код ломается сразу после error_log('1....I\'M GOING TO BREAK...');, Самое смешное, что мне удалось обойти всю библиотеку с помощью curl и получить токен доступа, который имеет вид:

[02-Nov-2017 14:06:38 Europe/Helsinki] Array
(
    [access_token] => ya29.Glv3B...xlSKIL_N67PE4...PYGDjZo-jD8v...ITFTnU
    [expires_in] => 2017-11-02 15:06:37
    [id_token] => eyJhbGciOiJImt...tlf_-Y1hXoCVSp...Ve_bK8F-jTCt...zg
    [token_type] => Bearer
)

но я не могу найти конечную точку чтобы получить данные пользователя: что-то вроде этого [иногда] ломается [я посчитал до 90% ошибок]

public function userinfo() {
   $access_token = $this->access['access_token'];
   $url = 'https://www.googleapis.com/plus/v1/people/me';
   $ch = curl_init();      
   curl_setopt($ch, CURLOPT_URL, $url);      
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
   curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
   curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer '. $access_token));
   $data = json_decode(curl_exec($ch), true);
   $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);      
   if ($http_code != 200)
      throw new \ErrorException(__CLASS__ . "::userinfo() is called but failed to get user information!", 0, 1);
   $this->data = $data;
}

благодаря этому отличному посту.

Есть идеи, что не так с официальной библиотекой php? Что-то мне не хватает в консоли?

Спасибо.

1 ответ

После долгих поисков,

как говорится в документации Google:

Вам не нужно устанавливать какие-либо библиотеки, чтобы иметь возможность напрямую вызывать конечные точки OAuth 2.0.

Также в OpenId:

В этом документе описывается наша реализация OAuth 2.0 для аутентификации, которая соответствует спецификации OpenID Connect и сертифицирована OpenID. Документация, приведенная в разделе Использование OAuth 2.0 для доступа к API Google, также применима к этому сервису.

Итак, я отправил все библиотеки в свою корзину и использую REST с cURL для выполнения запросов к серверу. Давайте определим класс GoogleToken содержит свойства, связанные с нашими запросами к серверам Google, такие как URL, идентификатор клиента, секрет клиента и т. д.

файл GoogleToken.php

<?php
namespace test;

/**
 * Google authentication Config class
 */
class GoogleToken {
   /** See the list: https://developers.google.com/identity/protocols/OAuth2WebServer#creatingclient */
   /** the application/client id */
   const appId = '';
   /** the application/client secret */
   const appSecret = '';
   /** the application/client redirect uri after login */
   const loginUri = 'http://localhost/auth/google/login';
   /** the application/client redirect uri after logout */
   const logoutUri = 'http://localhost/auth/google/logout';
   /** use 'online' to get the access token!
    * use 'offline' to get a refresh token along with the access token, 
    * see also 'prompt'!
    */
   const access = 'offline';
   /** the application/client approval prompt [auto|force]
    * use 'force' to get  a refresh token!
    */
   const prompt = 'force';
   /** the application/client access permissions
    *  [openid profile email|see https://developers.google.com/identity/protocols/googlescopes]
    */
   const scopes = 'openid profile email';

   public function __construct() {}
}

Приведенный выше класс следует рассматривать как токен программирования, который помогает другим классам достигать своих целей (возможно, это новая парадигма, вы можете оценить ее). На самом деле это более сложный класс, который может служить нашим целям с помощью методов ввода-вывода данных, облегчения управления пользователем без касания файлов php и т. Д.

Давайте создадим класс, который отправляет запросы на серверы Google и сохраняет (временно) ответы, фактические токены, которые отправляет Google:

файл GoogleHttpClient.php

Скелет этого класса:

public function authenticate($code)
public function verify($access_token)
public function validate($verify_token, $user)
public function refresh($refresh_token)
public function revokeToken($access_token)
public function userInfo($access_token)
public function createAuthUrl()

Все методы принимают аргументы, потому что при перенаправлениях то, что хранится внутри, теряется, и поэтому нам необходим механизм хранения для предоставления этих аргументов. файл test.php предоставляет верхний слой, который хранит свойства класса и вызывает их при необходимости. Интересная часть этого метода authenticate() следует сделать по порядку:

  1. запрос проверки

  2. запрос информации о пользователе

и не наоборот, или вы будете испытывать задержки / ошибки от Google. Класс GoogleHttpClient:

<?php
namespace test;

/**
 * The GoogleHttpClient is a helper class.
 * 
 * As this class is called between different calls, it needs access to a state
 * storage mechanism like sessions.
 * Even though the methods try to abstract away the complexity of the calls by 
 * a logical organisation of the actions involved, you are responsible for the 
 * order or context under which they are called.
 */
class GoogleHttpClient {
   /** @var GoogleToken $token The GoogleToken class object */
   protected $token;

   /** 
    * @var string[] $auth The authorization data returned by Google in the form:
    * Array(
    *    [access_token] => xxx
    *    [expires_in] => [unix timestamp]
    *    [id_token] => xxx.xxx.xxx
    *    [refresh_token] => xxx
    *    [token_type] => Bearer
    * )
    * 
    * This property should be stored in session (per user) in case we need a 
    * verification or refresh.
    */
   protected $auth;

   /** 
    * @var string[] $verify The response data returned by Google in the form:
    * Array(
    *    [issued_to] => xxx.apps.googleusercontent.com (@see GoogleToken['values']['appId'])
    *    [audience] => xxx.apps.googleusercontent.com (@see GoogleToken['values']['appId'])
    *    [user_id] => xxx
    *    [scope] => xxx (@see createAuthUrl())
    *    [expires_in] => xxx (<= 3600)
    *    [email] => xxx
    *    [verified_email] => 1
    *    [access_type] => xxx (@see createAuthUrl())
    * )
    * 
    * You shouldn't store this property in session.
    */
   protected $verify;

   /** 
    * @var string[] $user The user data returned by Google.
    * 
    * You should verify this property with your native authorization system and
    * integrate with it finally.
    */
   protected $user;

   /** @var string[] $error The error returned by Google */
   protected $error;

   /** 
    * @var boolean $log If you should log errors and assigned properties for debuging
    */
   protected $log;

   const INVALID_TOKEN = 1;

   /**
    * Constructor.
    * 
    * preconditions: a GoogleToken should be passed.
    *
    * postconditions: 
    * 
    * @param GoogleToken $token The GoogleToken that contains initialization values
    * @param boolean $log True, if we want to log errors and assigned properties
    * 
    * @return GoogleHttpClient A GoogleHttpClient object
    */
   public function __construct(GoogleToken $token, $log = false) {
      $this->token = $token;
      $this->log = $log;
   }

   /**
    * It makes a authentication request to Google.
    * 
    * preconditions: after the call to Google at this link, @see createAuthUrl(), 
    *    Google responds by making a call back with a unique id that should be
    *    passed as argument.
    *
    * postconditions: it sets properties $auth, $user, $verify and $error. 
    *    On error, $auth, $verify and $error contain the responses returned  by 
    *    Google depending at the method where error appeared, @see verify(), @see validate().
    *    If the authentication succeeds then, $error is null.
    * 
    * @param string $code A unique request id issued by Google
    * 
    * @return boolean True on success
    */
   public function authenticate($code) {
      $client_id = $this->token::appId;
      $client_secret  = $this->token::appSecret;
      $redirect_uri = $this->token::loginUri;
      $url = 'https://accounts.google.com/o/oauth2/token';
      $curlPost = 'client_id=' . $client_id . '&client_secret=' . $client_secret . '&redirect_uri=' . $redirect_uri . '&code='. $code . '&grant_type=authorization_code';
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
      curl_setopt($ch, CURLOPT_POST, 1);     
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);   
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);    
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::authenticate() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         if ($this->log)
            error_log(__CLASS__ . '::auth='.print_r($data, true));
         $this->verify($data['access_token']);
         $this->user = $this->userInfo($data['access_token']);
         $this->validate($this->verify, $this->user);
      }
      $this->auth = $data;
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It requests for a verification token from Google.
    * 
    * preconditions: it is called from @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: it sets property $verify. 
    *    On error, $verify and $error contain the responses returned  by Google.
    *    If the request succeeds then, $error is null.
    * 
    * @param string $access_token Google's authorization response under key 'access_token'
    * 
    * @return boolean True on success
    */
   public function verify($access_token) {
      $client_id = $this->token::appId;
      $client_secret  = $this->token::appSecret; 
      $url = 'https://www.googleapis.com/oauth2/v2/tokeninfo';
      $curlPost = 'access_token='. $access_token;
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
      curl_setopt($ch, CURLOPT_POST, 1);     
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);   
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);    
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::verify() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         if ($this->log)
            error_log(__CLASS__ . '::verify='.print_r($data, true));
      }
      $this->verify = $data;
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It compares properties $verify with $user and GoogleToken.
    * 
    * preconditions: it is called from @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: on failed validation, $error contains a custom error with fields:
    *    -error_id: a number
    *    -error_description: a message.
    *    If the verification succeeds then, $error is null.
    * 
    * @param string[] $verify_token Google's verification response token
    * @param string $user Google's user info response
    * 
    * @return boolean True on success
    */
   public function validate($verify_token, $user) {
      if (($verify_token['user_id'] != $user['id']) || ($verify_token['email'] != $user['email']) || ($verify_token['issued_to'] != $this->token::appId)) {
         $this->error = array('error_id' => self::INVALID_TOKEN, 'error_description' => 'Access token does not pass validation!');
         if ($this->log)
            error_log(__CLASS__ . '::error='.print_r($this->error, true));
      }
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It is called when property $auth has expired.
    * 
    * preconditions: authentication has been run, @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: it re-sets property $auth. 
    *    On error, $auth and $error contain the responses returned  by Google.
    *    If the refresh succeeds then, $error is null.
    * 
    * @param string $refresh_token Google's authorization response under key 'refresh_token'
    * 
    * @return boolean True on success
    */
   public function refresh($refresh_token) {
      $client_id = $this->token::appId;
      $client_secret  = $this->token::appSecret; 
      $url = 'https://accounts.google.com/o/oauth2/token';
      $curlPost = 'client_id=' . $client_id . '&client_secret=' . $client_secret . '&refresh_token='. $refresh_token . '&grant_type=refresh_token';
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
      curl_setopt($ch, CURLOPT_POST, 1);     
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);   
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);    
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::refresh() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         if ($this->log)
            error_log(__CLASS__ . '::auth='.print_r($data, true));
      }
      $this->auth = $data;
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It un-registers application from user's account but id does not (and cannot)
    * logout user from Google.
    * 
    * preconditions: authentication has been run, @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: it un-registers application from user's account. 
    *    On error, $error contains the response returned  by Google.
    *    If the revoke succeeds then, all properties are nullified.
    * 
    * @param string $access_token Google's authorization response under key 'access_token'
    * 
    * @return boolean True on success
    */
   public function revokeToken($access_token) {
      $client_id = $this->token::appId;
      $client_secret  = $this->token::appSecret; 
      $url = 'https://accounts.google.com/o/oauth2/revoke';
      $curlPost = 'token=' . $access_token;
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    
      curl_setopt($ch, CURLOPT_POST, 1);     
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);   
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);    
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::revokeToken() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         $this->auth = null;
         $this->user = null;
         $this->verify = null;
         $this->error = null;
         if ($this->log)
            error_log(__CLASS__ . '::revokeToken() run and erased all properties!');
      }
      if ($this->error)
         return false;
      else
         return true;
   }

   /**
    * It is called from @see authenticate() or you can call it independently at 
    * a later time.
    * 
    * preconditions: authentication has been run, @see authenticate().
    *    Calling this method from redirects means all properties are null and we 
    *    pass session data.
    *
    * postconditions: it sets property $user. 
    *    On error, $user and $error contain the responses returned  by Google.
    *    If the request succeeds then, $error is null.
    * 
    * @param string $access_token Google's authorization response under key 'access_token'
    * 
    * @return string[] A hash array of user's data
    */
   public function userInfo($access_token) {
      /** format of data:
       * Array(
       * [id] => xxx
       * [email] => xxx@gmail.com
       * [verified_email] => 1
       * [name] => xxx xxx
       * [given_name] => xxx
       * [family_name] => xxx
       * [picture] => https://lh6.googleusercontent.com/.../photo.jpg?sz=50
       * [locale] => en
       * )
       */
      $url = 'https://www.googleapis.com/userinfo/v2/me';
      $ch = curl_init();      
      curl_setopt($ch, CURLOPT_URL, $url);      
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer '. $access_token));
      $data = json_decode(curl_exec($ch), true);
      $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);      
      if ($http_code != 200) {
         if ($this->log)
            error_log(__CLASS__ . '::userInfo() error: http code not 200. Responded: '.print_r($data, true));
         $this->error = $data;
      } else {
         if ($this->log)
            error_log(__CLASS__ . '::user='.print_r($data, true));
      }
      $this->user = $data;
      return $this->user;
   }

   /**
    * Creates a authentication link to Google servers.
    */
   public function createAuthUrl() {
      $scopes = \urlencode($this->token::scopes);
      $redirect = \urlencode($this->token::loginUri);
      $appId = $this->token::appId;
      $access = $this->token::access;
      $prompt = $this->token::prompt;
      return "https://accounts.google.com/o/oauth2/auth?scope={$scopes}&redirect_uri={$redirect}&response_type=code&client_id={$appId}&access_type={$access}&approval_prompt={$prompt}";
   }

   /**
    * Returns property $token
    */
   public function getToken() {
      return $this->token;
   }

   /**
    * Returns property $auth
    */
   public function getAuthToken() {
      return $this->auth;
   }

   /**
    * Returns property $verify
    */
   public function getVerifyToken() {
      return $this->verify;
   }

   /**
    * Returns property $user
    */
   public function getUser() {
      return $this->user;
   }

   /**
    * Returns property $error
    */
   public function getError() {
      return $this->error;
   }
}

Теперь мы можем позвонить в Google и создать приложение для входа:

файл test.php

<?php
namespace test;

require_once __DIR__ . '/GoogleToken.php';
require_once __DIR__ . '/GoogleHttpClient.php';

if (\session_status() != PHP_SESSION_ACTIVE) {
   session_start();
}

$whoami = $_SERVER['REQUEST_URI'];
$login = $logout = false;
if (\strpos($whoami, '/login') !== false)
   $login = true;
if (\strpos($whoami, '/logout') !== false)
   $logout = true;

$token = new GoogleToken();
$client = new GoogleHttpClient($token, true);   // enable log
$loginUri = GoogleToken::loginUri;
$logoutUri = GoogleToken::logoutUri;

/* emulates...
 * LOGIN endpoint
 * --------------
 */
if ($login) {
  if (isset($_GET['code'])) {
      if ($client->authenticate($_GET['code']) {
         $_SESSION['google']['auth'] = $auth;
         $_SESSION['google']['auth']['expires_in'] = \date("Y-m-d H:i:s", \time() + $_SESSION['google']['auth']['expires_in']);
         $_SESSION['google']['user'] = $user;
      }
   }
   header('Location: ' . filter_var('http://localhost', FILTER_SANITIZE_URL));
}

/* emulates...
 * LOGOUT endpoint
 * ---------------
 */
if ($logout) {
  $client->revokeToken($_SESSION['google']['auth']['access_token']);
  unset($_SESSION['google']);
  header('Location: ' . filter_var('http://localhost', FILTER_SANITIZE_URL));
}

$userData = '';

/* emulates...
 * Redirect from:
 * --------------
 * - LOGIN endpoint
 */
if (isset($_SESSION['google'])) {
   if(\time() > \strtotime($_SESSION['google']['auth']['expires_in'])) {
      if(!$client->refresh($_SESSION['google']['auth']['access_token'])) {
         $client->revokeToken();
         unset($_SESSION['google']);
         header('Location: ' . filter_var('http://localhost', FILTER_SANITIZE_URL));
      }
   }
   $userData = $_SESSION['google']['user'];

/* emulates...
 * INITIAL endpoint
 * ----------------
 * or,
 * Redirect from:
 * --------------
 * - LOGOUT endpoint or,
 * - failed REFRESH
 */
} else {
   $authUrl = $client->createAuthUrl();
}

$out0 = <<<EOT
<html>
   <head> 
      <title>Google REST OAuth v.2.0 Login test</title>
   </head>
   <body>
EOT;
if (isset($authUrl)) {
   $d = \urldecode($authUrl);
   $out0 .= <<<EOT
      <p>decoded authUrl = '{$d}'</p>
EOT;
}
$out0 .= <<<EOT
      <h2>PHP Google OAuth 2.0 Login</h2>
EOT;
if (isset($authUrl)) {
   $out0 .= <<<EOT
      <p><a href="{$authUrl}">Login with Google API</a></p>
EOT;
} else {
    $out0 .= <<<EOT
      <p>Welcome <img src="{$userData['picture']}" style="width:50px;height:50px;"></img> {$userData['name']}.</p>
      <p>Your email: {$userData['email']}</p>
      <p><a href={$logoutUri}>Logout</a></p>
EOT;
}
$e = \nl2br(\htmlspecialchars(\print_r($_SESSION, true)));
$out0 .= <<<EOT
      <p><h3>SESSION:</h3></p>
      <p>{$e}</p>
   </body>
</html>
EOT;
echo $out0;

Как мы видим класс GoogleHttpClient делает всю работу, кроме файла test.php на самом деле должен быть заменен другим слоем, который обрабатывает наши конечные точки / запросы поверх GoogleHttpClient,

Повеселись!

Другие вопросы по тегам