如何让我的 php 应用程序永远不会要求 Gmail API 验证码?
How do I make my php app never ask for the Gmail API verification code?
我希望该应用程序自动运行,但在我们测试期间,如果我们在周末度过而不在周一打扰它,该应用程序做的第一件事是:
在浏览器中打开以下link:
https://accounts.google.com/o/oauth2/(...)
输入验证码:
我曾经在网上的某个地方看到,有必要将下面代码中的选项加粗;但没有按预期工作。
use GuzzleHttp\Client;
require __DIR__ . '/vendor/autoload.php';
if (php_sapi_name() != 'cli') {
throw new Exception('This application must be run on the command line.');
}
// @return Google_Client the authorized client object
function getClient()
{
$guzzleClient = new GuzzleHttp\Client([
'proxy' => '<my_ip_proxy_pfsense>:<port>',
'verify' => false,
]);
$client = new Google_Client();
$client->setApplicationName('RS Gmail Check API PHP');
$client->setScopes(Google_Service_Gmail::GMAIL_READONLY);
$client->setAuthConfig('credentials.json');
**$client->setAccessType('offline');**
// Using "consent" ensures that your application always receives a refresh token.
// If you are not using offline access, you can omit this.
**$client->setApprovalPrompt("consent");
$client->setIncludeGrantedScopes(true); // incremental auth**
//
$client->setPrompt('select_account consent');
$client->setHttpClient($guzzleClient);
$tokenPath = '/opt/gmail-check/token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
} else {
$authUrl = $client->createAuthUrl();
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
if (isset($_GET['code'])) {
$authCode = $_GET['code'];
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
header('Location: ' . filter_var($this->redirectUri,
FILTER_SANITIZE_URL));
if(!file_exists(dirname($this->tokenFile))) {
mkdir(dirname($this->tokenFile), 0700, true);
}
file_put_contents($this->tokenFile, json_encode($accessToken));
}else{
exit('No code found');
}
}
$client->setAccessToken($accessToken);
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
} // */
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Gmail($client);
// Print the labels in the user's account.
$user = 'me';
$inboxMessage = [];
function decodeBody($body) {
$rawData = $body;
$sanitizedData = strtr($rawData,'-_', '+/');
$decodedMessage = base64_decode($sanitizedData);
if(!$decodedMessage){
$decodedMessage = FALSE;
}
return $decodedMessage;
}
$list = $service->users_messages->listUsersMessages($user, ['maxResults' => 10, 'q' => $search]);
try{
foreach ($list->getMessages() as $mlist) {
$message_id = $mlist->id;
$optParamsGet2['format'] = 'full';
$single_message = $service->users_messages->get('me', $message_id, $optParamsGet2);
$payload = $single_message->getPayload();
$headers = $single_message->getPayload()->getHeaders();
$snippet = $single_message->getSnippet();
foreach($headers as $single) {
if ($single->getName() == 'Subject') {
$message_subject = $single->getValue();
}
else if ($single->getName() == 'Date') {
$message_date = $single->getValue();
$message_date = date('M jS Y h:i A', strtotime($message_date));
}
else if ($single->getName() == 'From') {
$message_sender = $single->getValue();
$message_sender = str_replace('"', '', $message_sender);
}
}
$inboxMessage = [
'messageId' => $message_id,
'messageSnippet' => $snippet,
'messageSubject' => $message_subject,
'messageDate' => $message_date,
'messageSender' => $message_sender
];
// With no attachment, the payload might be directly in the body, encoded.
$body = $payload->getBody();
$FOUND_BODY = decodeBody($body['data']);
// If we didn't find a body, let's look for the parts
if(!$FOUND_BODY) {
$parts = $payload->getParts();
foreach ($parts as $part) {
if($part['body']) {
$FOUND_BODY = decodeBody($part['body']->data);
}
// Last try: if we didn't find the body in the first parts,
// let's loop into the parts of the parts (as @Tholle suggested).
if($part['parts'] && !$FOUND_BODY) {
foreach ($part['parts'] as $p) {
// replace 'text/html' by 'text/plain' if you prefer
if($p['mimeType'] === 'text/html' && $p['body']) {
$FOUND_BODY = decodeBody($p['body']->data);
}
}
}
}
}
更新:这个周末我用 Gmail API 对另一个项目做了一些测试,但基于我在服务中使用的相同代码,甚至尽管我花了 5 个多小时没有 运行 该应用程序,但没有时间被多次询问令牌。 .最显着的区别是在服务中 GuzzleClient 配置为使用代理(这将是我们的 pfsense 防火墙)。由于代理,我在工作中遇到令牌过期问题吗?
- 您需要对您的应用进行身份验证才能获得您的 access token
- 这是您第一次 运行 您的应用程序或更改范围时所必需的
- 您的访问令牌将在特定时间后过期
- 为了避免在您的访问令牌过期时重新进行身份验证,您需要将 refresh token 合并到您的应用程序中
- 刷新令牌将在每次访问令牌到期之前自动刷新您的访问令牌
- 不会每次都提示重新认证
- Further reading
如何将刷新令牌合并到您的应用程序中
关注 Google API quickstart PHP。示例:
<?php
require __DIR__ . '/vendor/autoload.php';
if (php_sapi_name() != 'cli') {
throw new Exception('This application must be run on the command line.');
}
/**
* Returns an authorized API client.
* @return Google_Client the authorized client object
*/
function getClient()
{
$client = new Google_Client();
$client->setApplicationName('Gmail API PHP Quickstart');
$client->setScopes(Google_Service_Gmail::GMAIL_READONLY);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Gmail($client);
// Print the labels in the user's account.
$user = 'me';
$results = $service->users_labels->listUsersLabels($user);
if (count($results->getLabels()) == 0) {
print "No labels found.\n";
} else {
print "Labels:\n";
foreach ($results->getLabels() as $label) {
printf("- %s\n", $label->getName());
}
}
更新:
在你提供完整的代码后,我看到除了需要的授权流程,你还有以下备用行:
else {
$authUrl = $client->createAuthUrl();
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
if (isset($_GET['code'])) {
$authCode = $_GET['code'];
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
header('Location: ' . filter_var($this->redirectUri,
FILTER_SANITIZE_URL));
if(!file_exists(dirname($this->tokenFile))) {
mkdir(dirname($this->tokenFile), 0700, true);
}
file_put_contents($this->tokenFile, json_encode($accessToken));
}else{
exit('No code found');
}
}
所以,例如行
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
创建授权 URL 并提示进行身份验证,但请注意
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
您又在做同样的事情 - 这可以解释为什么提示您进行身份验证的频率比预期的要高。
此外,您还有
行
$client->setAccessToken($accessToken);
两次。
如果您删除备用代码并删除您的令牌文件(以触发新的身份验证流程),您的问题应该得到解决。
我希望该应用程序自动运行,但在我们测试期间,如果我们在周末度过而不在周一打扰它,该应用程序做的第一件事是:
在浏览器中打开以下link: https://accounts.google.com/o/oauth2/(...) 输入验证码:
我曾经在网上的某个地方看到,有必要将下面代码中的选项加粗;但没有按预期工作。
use GuzzleHttp\Client;
require __DIR__ . '/vendor/autoload.php';
if (php_sapi_name() != 'cli') {
throw new Exception('This application must be run on the command line.');
}
// @return Google_Client the authorized client object
function getClient()
{
$guzzleClient = new GuzzleHttp\Client([
'proxy' => '<my_ip_proxy_pfsense>:<port>',
'verify' => false,
]);
$client = new Google_Client();
$client->setApplicationName('RS Gmail Check API PHP');
$client->setScopes(Google_Service_Gmail::GMAIL_READONLY);
$client->setAuthConfig('credentials.json');
**$client->setAccessType('offline');**
// Using "consent" ensures that your application always receives a refresh token.
// If you are not using offline access, you can omit this.
**$client->setApprovalPrompt("consent");
$client->setIncludeGrantedScopes(true); // incremental auth**
//
$client->setPrompt('select_account consent');
$client->setHttpClient($guzzleClient);
$tokenPath = '/opt/gmail-check/token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
} else {
$authUrl = $client->createAuthUrl();
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
if (isset($_GET['code'])) {
$authCode = $_GET['code'];
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
header('Location: ' . filter_var($this->redirectUri,
FILTER_SANITIZE_URL));
if(!file_exists(dirname($this->tokenFile))) {
mkdir(dirname($this->tokenFile), 0700, true);
}
file_put_contents($this->tokenFile, json_encode($accessToken));
}else{
exit('No code found');
}
}
$client->setAccessToken($accessToken);
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
} // */
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Gmail($client);
// Print the labels in the user's account.
$user = 'me';
$inboxMessage = [];
function decodeBody($body) {
$rawData = $body;
$sanitizedData = strtr($rawData,'-_', '+/');
$decodedMessage = base64_decode($sanitizedData);
if(!$decodedMessage){
$decodedMessage = FALSE;
}
return $decodedMessage;
}
$list = $service->users_messages->listUsersMessages($user, ['maxResults' => 10, 'q' => $search]);
try{
foreach ($list->getMessages() as $mlist) {
$message_id = $mlist->id;
$optParamsGet2['format'] = 'full';
$single_message = $service->users_messages->get('me', $message_id, $optParamsGet2);
$payload = $single_message->getPayload();
$headers = $single_message->getPayload()->getHeaders();
$snippet = $single_message->getSnippet();
foreach($headers as $single) {
if ($single->getName() == 'Subject') {
$message_subject = $single->getValue();
}
else if ($single->getName() == 'Date') {
$message_date = $single->getValue();
$message_date = date('M jS Y h:i A', strtotime($message_date));
}
else if ($single->getName() == 'From') {
$message_sender = $single->getValue();
$message_sender = str_replace('"', '', $message_sender);
}
}
$inboxMessage = [
'messageId' => $message_id,
'messageSnippet' => $snippet,
'messageSubject' => $message_subject,
'messageDate' => $message_date,
'messageSender' => $message_sender
];
// With no attachment, the payload might be directly in the body, encoded.
$body = $payload->getBody();
$FOUND_BODY = decodeBody($body['data']);
// If we didn't find a body, let's look for the parts
if(!$FOUND_BODY) {
$parts = $payload->getParts();
foreach ($parts as $part) {
if($part['body']) {
$FOUND_BODY = decodeBody($part['body']->data);
}
// Last try: if we didn't find the body in the first parts,
// let's loop into the parts of the parts (as @Tholle suggested).
if($part['parts'] && !$FOUND_BODY) {
foreach ($part['parts'] as $p) {
// replace 'text/html' by 'text/plain' if you prefer
if($p['mimeType'] === 'text/html' && $p['body']) {
$FOUND_BODY = decodeBody($p['body']->data);
}
}
}
}
}
更新:这个周末我用 Gmail API 对另一个项目做了一些测试,但基于我在服务中使用的相同代码,甚至尽管我花了 5 个多小时没有 运行 该应用程序,但没有时间被多次询问令牌。 .最显着的区别是在服务中 GuzzleClient 配置为使用代理(这将是我们的 pfsense 防火墙)。由于代理,我在工作中遇到令牌过期问题吗?
- 您需要对您的应用进行身份验证才能获得您的 access token
- 这是您第一次 运行 您的应用程序或更改范围时所必需的
- 您的访问令牌将在特定时间后过期
- 为了避免在您的访问令牌过期时重新进行身份验证,您需要将 refresh token 合并到您的应用程序中
- 刷新令牌将在每次访问令牌到期之前自动刷新您的访问令牌
- 不会每次都提示重新认证
- Further reading
如何将刷新令牌合并到您的应用程序中
关注 Google API quickstart PHP。示例:
<?php
require __DIR__ . '/vendor/autoload.php';
if (php_sapi_name() != 'cli') {
throw new Exception('This application must be run on the command line.');
}
/**
* Returns an authorized API client.
* @return Google_Client the authorized client object
*/
function getClient()
{
$client = new Google_Client();
$client->setApplicationName('Gmail API PHP Quickstart');
$client->setScopes(Google_Service_Gmail::GMAIL_READONLY);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Gmail($client);
// Print the labels in the user's account.
$user = 'me';
$results = $service->users_labels->listUsersLabels($user);
if (count($results->getLabels()) == 0) {
print "No labels found.\n";
} else {
print "Labels:\n";
foreach ($results->getLabels() as $label) {
printf("- %s\n", $label->getName());
}
}
更新:
在你提供完整的代码后,我看到除了需要的授权流程,你还有以下备用行:
else {
$authUrl = $client->createAuthUrl();
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
if (isset($_GET['code'])) {
$authCode = $_GET['code'];
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
header('Location: ' . filter_var($this->redirectUri,
FILTER_SANITIZE_URL));
if(!file_exists(dirname($this->tokenFile))) {
mkdir(dirname($this->tokenFile), 0700, true);
}
file_put_contents($this->tokenFile, json_encode($accessToken));
}else{
exit('No code found');
}
}
所以,例如行
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
创建授权 URL 并提示进行身份验证,但请注意
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
您又在做同样的事情 - 这可以解释为什么提示您进行身份验证的频率比预期的要高。
此外,您还有
行$client->setAccessToken($accessToken);
两次。
如果您删除备用代码并删除您的令牌文件(以触发新的身份验证流程),您的问题应该得到解决。