具有公共访问权限的 Web 应用程序的 Google OAuth 2.0 刷新令牌
Posted
技术标签:
【中文标题】具有公共访问权限的 Web 应用程序的 Google OAuth 2.0 刷新令牌【英文标题】:Google OAuth 2.0 refresh token for web application with public access 【发布时间】:2014-10-20 22:31:50 【问题描述】:我收到以下错误:
OAuth 2.0 访问令牌已过期,刷新令牌不可用。自动批准的响应不会返回刷新令牌
我有一个只有我的服务器才能访问的 Web 应用程序,初始身份验证工作正常,但一个小时后,弹出上述错误消息。我的脚本如下所示:
require_once 'Google/Client.php';
require_once 'Google/Service/Analytics.php';
session_start();
$client = new Google_Client();
$client->setApplicationName("Client_Library_Examples");
$client->setDeveloperKey("MY_API_KEY");
$client->setClientId('MY_CLIENT_ID.apps.googleusercontent.com');
$client->setClientSecret('MY_KEY');
$client->setRedirectUri('MY_REDIRECT_URI');
$client->setScopes(array('https://www.googleapis.com/auth/gmail.readonly'));
if (isset($_GET['code']))
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
if (!$client->getAccessToken() && !isset($_SESSION['token']))
$authUrl = $client->createAuthUrl();
print "<a class='login' href='$authUrl'>Connect Me!</a>";
如何为此设置刷新令牌?
编辑 1:我也尝试使用服务帐户执行此操作。我遵循了 GitHub 上提供的文档:
https://github.com/google/google-api-php-client/blob/master/examples/service-account.php
我的脚本如下所示:
session_start();
include_once "templates/base.php";
require_once 'Google/Client.php';
require_once 'Google/Service/Gmail.php';
$client_id = 'MY_CLIENT_ID.apps.googleusercontent.com';
$service_account_name = 'MY_EMAIL_ADDRESS@developer.gserviceaccount.com ';
$key_file_location = 'Google/MY_KEY_FILE.p12';
echo pageHeader("Service Account Access");
if ($client_id == ''
|| !strlen($service_account_name)
|| !strlen($key_file_location))
echo missingServiceAccountDetailsWarning();
$client = new Google_Client();
$client->setApplicationName("Client_Library_Examples");
$service = new Google_Service_Gmail($client);
if (isset($_SESSION['service_token']))
$client->setAccessToken($_SESSION['service_token']);
$key = file_get_contents($key_file_location);
$cred = new Google_Auth_AssertionCredentials(
$service_account_name,
array('https://www.googleapis.com/auth/gmail.readonly'),
$key
);
$client->setAssertionCredentials($cred);
if($client->getAuth()->isAccessTokenExpired())
$client->getAuth()->refreshTokenWithAssertion($cred);
这将返回以下消息:
刷新 OAuth2 令牌时出错,消息:' "error" : "invalid_grant" ''
跟踪此代码到其源代码后,可以在Google/Auth/OAuth2.php
找到
有问题的方法,refreshTokenRequest
:
private function refreshTokenRequest($params)
$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);
这意味着$code
变量为NULL。
我在 SO 上找到了这篇文章:
Error refreshing the OAuth2 token “error” : “invalid_grant”
并且可以看到仍然没有首选的解决方案。 这让我发疯了。这方面的文档很少甚至没有,如果有人有解决方案,我相信我不是唯一一个在寻找它的人。
【问题讨论】:
【参考方案1】:每个access_token在几秒后过期,需要通过refresh_token刷新,“离线访问”就是你要找的。您可以在此处查看文档:
https://developers.google.com/accounts/docs/OAuth2WebServer#offline
要获得一个 refresh_token,您只需运行一次此代码:
require_once 'Google/Client.php';
$client = new Google_Client();
$client->setClientId('MY_CLIENT_ID.apps.googleusercontent.com');
$client->setClientSecret('MY_KEY');
$client->setRedirectUri('MY_REDIRECT_URI');
//next two line added to obtain refresh_token
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setScopes(array('https://www.googleapis.com/auth/gmail.readonly'));
if (isset($_GET['code']))
$credentials = $client->authenticate($_GET['code']);
/*TODO: Store $credentials somewhere secure */
else
$authUrl = $client->createAuthUrl();
print "<a class='login' href='$authUrl'>Connect Me!</a>";
$credentials
包括访问令牌和刷新令牌。您必须存储它以随时获取新的访问令牌。所以当你想调用 api 时:
require_once 'Google/Client.php';
require_once 'Google/Service/Gmail.php';
/*TODO: get stored $credentials */
$client = new Google_Client();
$client->setClientId('MY_CLIENT_ID.apps.googleusercontent.com');
$client->setRedirectUri('MY_REDIRECT_URI');
$client->setClientSecret('MY_KEY');
$client->setScopes(array('https://www.googleapis.com/auth/gmail.readonly'));
$client->setAccessToken($credentials);
$service = new Google_Service_Gmail($client);
【讨论】:
非常感谢!完美运行。我要求对帖子进行编辑以删除您在第二个 API 调用中json_decode
$credentialsJson 的位,因为 OAuth2.php
setAccessToken
第 179 行为您执行此操作,如果您事先执行此操作,则会引发致命错误。
是的,你是对的,我还删除了错误的第一个脚本的第 14 行。我想json_encode($credentials)
但authenticate
为我们做这件事。
$access_token = $client->getAccessToken();
和 $credentials = $client->authenticate($_GET['code']);
相同。我的 $credentials
不包含 refresh_token 值。只包含access_token、token_type、expires_in、created
@Mutlu 正如我所说,你必须设置 $client->setAccessType('offline');
和 $client->setApprovalPrompt('force');
来强制你的 $credentials
包含 refresh_token。以上是关于具有公共访问权限的 Web 应用程序的 Google OAuth 2.0 刷新令牌的主要内容,如果未能解决你的问题,请参考以下文章
单流:通过 Google oAuth 登录用户并授予离线/服务器访问权限?
如何建立从GAE(具有公共访问权限)到私有GKE集群的安全连接。