谷歌日历 API - PHP
我目前正在将 Google Calendar API 用于网络应用程序.但是,每隔一小时,我都会收到一个链接以验证快速入门访问权限.有谁知道如何解决这个问题?
I am currently using the Google Calendar API for a web application. However, every hour, I am prompted with a link to verify quickstart access. Does anyone know how to fix this?
详情:
- 我创建了一个新的 gmail id:redu@gmail.com
- redu@gmail.com 有关联的日历
- 我的基于 php 的 Web 应用程序需要使用日历执行以下操作:
- 为每个注册用户创建一个新日历(作为 redu@gmail.com 的附加日历)
- 为登录用户创建一个活动并将另一个注册用户添加为受邀者
我尝试过使用 OAUTH 和服务帐户,但没有成功.非常感谢任何帮助.
I have tried using OAUTH and service accounts with no luck. Any help is greatly appreciated.
以下是使用服务帐户凭据创建 Google_Client 和 Srvice 对象的代码
Below is the code that creates Google_Client and Srvice objects using service account's credentials
function __construct()
{
Service account based client creation.
$this->client = new Google_Client();
$this->client->setApplicationName("Redu");
$this->client->setAuthConfig(CREDENTIALS_PATH);
$this->client->setScopes([SCOPES]);
$this->client->setSubject('redu@gmail.com');
$this->client->setAccessType('offline');
$this->service = new Google_Service_Calendar($this->client);
}
当我尝试使用 $service 对象创建日历或创建事件时,我收到一条错误消息,指出未设置域范围的权限.但是,当我创建服务帐户时,我确实启用了全域委派.
When I try to use the $service object to create a calendar or create an event I get an error saying that domain wide permissions are not setup. However, when I created the service account I did enable domain wide delegation.
以下是我使用服务帐户密钥创建 Google_Client 并使用客户端为 redu@gmail.com 创建新日历的代码.请注意,我与 reduservice@subtle-breaker-280602.iam.gserviceaccount.com 共享了 redu@gmail.com 的日历,并将权限设置为管理更改和管理共享".我得到的错误在代码下方:
Below is my code to create a Google_Client using service account key and use the client to create a new calendar for redu@gmail.com. Note that I shared redu@gmail.com's calendar with reduservice@subtle-breaker-280602.iam.gserviceaccount.com and set the permission to "Manage Changes and Manage Sharing". The error I am getting is below the code:
require (__DIR__.'/../../../vendor/autoload.php');
define('CREDENTIALS_PATH', __DIR__ . '/redu_service_account_credentials.json');
define('SCOPES', Google_Service_Calendar::CALENDAR);
function createNewCalendar($userName) {
//Service account based client creation.
$client = new Google_Client();
$client->setApplicationName("REdu");
// path to the credentials file obtained upon creating key for service account
$client->setAuthConfig(CREDENTIALS_PATH);
$client->setScopes([SCOPES]);
$client->setSubject('redu@gmail.com');
$client->setAccessType('offline');
$service = new Google_Service_Calendar($client);
$calendar = new Google_Service_Calendar_Calendar();
$calendar->setSummary($userName);
$calendar->setTimeZone('America/Los_Angeles');
$createdCalendar = $service->calendars->insert($calendar);
// Make the newly created calendar public
$rule = new Google_Service_Calendar_AclRule();
$scope = new Google_Service_Calendar_AclRuleScope();
$scope->setType("default");
$scope->setValue("");
$rule->setScope($scope);
$rule->setRole("reader");
// Make the calendar public
$createdRule = $service->acl->insert($createdCalendar->getId(), $rule);
return $createdCalendar->getId();
}
错误:
Fatal error: Uncaught exception 'Google_Service_Exception' with message '{
"error": "unauthorized_client",
"error_description": "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested."
}'
推荐答案
OAUTH2 与服务帐户
Oauth2 和服务帐户是两个不同的东西.如果您尝试访问用户数据,请使用 oauth2.您提到的同意窗口将支持并要求他们授予您的应用程序访问其数据的权限.
OAUTH2 vs Service accounts
Oauth2 and service accounts are two different things. You use oauth2 if you are trying to access a users data. The consent window you mentioned will prop up and ask that they grant permission for your application to access their data.
另一方面,服务帐户是虚拟用户,可以预先批准访问由开发人员控制的数据.您可以与服务帐户共享日历,授予其访问该日历的权限,而无需以与用户相同的方式进行身份验证.
Service accounts on the other hand are dummy users who can be pre approved to access data you the developer control. You could share a calendar with a service account granting it access to that calendar it will no need to be authenticated in the same manner as a user.
永远不会弹出服务帐户并再次请求访问权限.
A service account will never popup and request access again.
问题是您的访问令牌即将过期.如果它过期,那么用户将需要再次授予您的应用程序访问其数据的权限.为了避免这种情况,我们使用刷新令牌并将其存储在会话变量中,当访问令牌过期时,我们只需请求一个新令牌.
The issue is that your access token is expiring. If it expires then the user will need to grant your application access to their data again. To avoid this we use a refresh token and store that in a session varable and when the acces stoken expires we just request a new one.
注意我如何请求 $client->setAccessType("offline");
这会给我一个刷新令牌.
Notice how i am requesting $client->setAccessType("offline");
this will give me a refresh token.
会话变量现在设置存储此数据
the session vars are now set storing this data
$_SESSION['access_token'] = $client->getAccessToken();
$_SESSION['refresh_token'] = $client->getRefreshToken();
然后我可以检查访问令牌是否过期,如果我刷新它
Then latter i can check if the access token is expired if so i refresh it
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$client->setAccessToken($client->getAccessToken());
$_SESSION['access_token'] = $client->getAccessToken();
}
oauth2callback.php
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/Oauth2Authentication.php';
// Start a session to persist credentials.
session_start();
// Handle authorization flow from the server.
if (! isset($_GET['code'])) {
$client = buildClient();
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$client = buildClient();
$client->authenticate($_GET['code']); // Exchange the authencation code for a refresh token and access token.
// Add access token and refresh token to seession.
$_SESSION['access_token'] = $client->getAccessToken();
$_SESSION['refresh_token'] = $client->getRefreshToken();
//Redirect back to main script
$redirect_uri = str_replace("oauth2callback.php",$_SESSION['mainScript'],$client->getRedirectUri());
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
身份验证.php
require_once __DIR__ . '/vendor/autoload.php';
/**
* Gets the Google client refreshing auth if needed.
* Documentation: https://developers.google.com/identity/protocols/OAuth2
* Initializes a client object.
* @return A google client object.
*/
function getGoogleClient() {
$client = getOauth2Client();
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
return $client;
}
/**
* Builds the Google client object.
* Documentation: https://developers.google.com/identity/protocols/OAuth2
* Scopes will need to be changed depending upon the API's being accessed.
* Example: array(Google_Service_Analytics::ANALYTICS_READONLY, Google_Service_Analytics::ANALYTICS)
* List of Google Scopes: https://developers.google.com/identity/protocols/googlescopes
* @return A google client object.
*/
function buildClient(){
$client = new Google_Client();
$client->setAccessType("offline"); // offline access. Will result in a refresh token
$client->setIncludeGrantedScopes(true); // incremental auth
$client->setAuthConfig(__DIR__ . '/client_secrets.json');
$client->addScope([YOUR SCOPES HERE]);
$client->setRedirectUri(getRedirectUri());
return $client;
}
/**
* Builds the redirect uri.
* Documentation: https://developers.google.com/api-client-library/python/auth/installed-app#choosingredirecturi
* Hostname and current server path are needed to redirect to oauth2callback.php
* @return A redirect uri.
*/
function getRedirectUri(){
//Building Redirect URI
$url = $_SERVER['REQUEST_URI']; //returns the current URL
if(strrpos($url, '?') > 0)
$url = substr($url, 0, strrpos($url, '?') ); // Removing any parameters.
$folder = substr($url, 0, strrpos($url, '/') ); // Removeing current file.
return (isset($_SERVER['HTTPS']) ? "https" : "http") . '://' . $_SERVER['HTTP_HOST'] . $folder. '/oauth2callback.php';
}
/**
* Authenticating to Google using Oauth2
* Documentation: https://developers.google.com/identity/protocols/OAuth2
* Returns a Google client with refresh token and access tokens set.
* If not authencated then we will redirect to request authencation.
* @return A google client object.
*/
function getOauth2Client() {
try {
$client = buildClient();
// Set the refresh token on the client.
if (isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']) {
$client->refreshToken($_SESSION['refresh_token']);
}
// If the user has already authorized this app then get an access token
// else redirect to ask the user to authorize access to Google Analytics.
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
// Set the access token on the client.
$client->setAccessToken($_SESSION['access_token']);
// Refresh the access token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$client->setAccessToken($client->getAccessToken());
$_SESSION['access_token'] = $client->getAccessToken();
}
return $client;
} else {
// We do not have access request access.
header('Location: ' . filter_var( $client->getRedirectUri(), FILTER_SANITIZE_URL));
}
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
?>
服务帐号代码
凭证文件不同,请勿混淆.
code for service account
The credential files are different dont mix them up.
function getServiceAccountClient() {
try {
// Create and configure a new client object.
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope([YOUR SCOPES HERE]);
return $client;
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
客户端无权使用此方法检索访问令牌,或客户端未获得任何请求范围的授权.
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.
客户端有 Oauth2 客户端和服务帐户客户端两种类型.您下载的 .json 文件因每个客户端而异.就像您将用于每个客户端的代码一样.你不能交换这个代码.
There are two types of clients Oauth2 clients and Service account clients. The .json file you download is diffrent for each client. As is the code you will use for each client. You cant interchange this code.
您得到的错误是您正在使用的客户端无法用于您正在使用的代码.再次尝试下载服务帐户的客户端密钥 .json.,
The error you are getting stats that the client you are using cant be used for the code you are using. Try to download the client secret .json for the service account again.,
相关文章