不通过浏览器获取 Google 访问令牌

Get Google access token not through the browser

问题已解决

最后,我通过从 google 帐户撤销(也就是通过帐户删除应用程序访问权限)获得了 refresh_token。

一切又恢复正常了,不需要像标题那样解决问题'Get Google access token not through the browser'。

可是为什么我这次成功了呢?因为这次我使用 curl 而不是 thephpleague/oauth2-client 方式来请求授权。我认为当我通过 thephpleague/oauth2-client.

请求身份验证时必须缺少一些参数

这是一个 curl 方法,在我 revoke/remove 从帐户访问应用程序后,我每次都得到 refresh_token。

<?
//Reference to dbjpanda/google-api.php(https://gist.github.com/dbjpanda/0ba3d73832b25d720398e8f1dce1359b)

$client_id = 'xxx.apps.googleusercontent.com';
$client_secret = 'xxxx';
$redirect_uri = 'https://example.com/get_token.php';
$end_point = 'https://accounts.google.com/o/oauth2/v2/auth';
$token_file="my-token.json";

$authUrl = $end_point.'?'.http_build_query([
    'client_id'              => $client_id,
    'redirect_uri'           => $redirect_uri,
    'scope'                  => 'https://mail.google.com/',
    'access_type'            => 'offline',
    'include_granted_scopes' => 'true',
    'response_type'          => 'code',
]);

echo '<a href = "'.$authUrl.'">Authorize</a></br>';

if ( !file_exists($token_file) ){
    
    if ( isset($_GET['code'])){
        $code = $_GET['code'];
    }else{
        return;
    } 

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL,"https://accounts.google.com/o/oauth2/token");
    curl_setopt($ch, CURLOPT_POST, TRUE);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/x-www-form-urlencoded']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
        'code'          => $code,
        'client_id'     => $client_id,
        'client_secret' => $client_secret,
        'redirect_uri'  => $redirect_uri,
        'grant_type'    => 'authorization_code',
    ]));
    
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close ($ch);
    
    file_put_contents($token_file, $response);

}else{
    //and then to use the refresh_token to get a new access_token
}

更新了问题

简而言之,如何不通过浏览器(即header('Location: '))自动获取google OAuth访问令牌?

我无法再获取刷新令牌,因为我在开发时超出了访问限制,我尝试了很多方法仍然无法获取刷新令牌。

原始问题

我已经成功设置 get_token.php 为我的网站(example.com)获取 Google OAuth2 for PHPMailer,我可以通过浏览器获取访问令牌(通过 select 我的 google 帐户登录访问)。

因为我已经超过开发刷新令牌的限制,所以我尝试使用另一个google帐户为我的网站创建api(example.com),撤销从 google 帐户,删除 OAuth 并创建另一个(因此有一个新的 clientId);但是 none 有效,我认为我再也不会获得刷新令牌了。

因为我的网站是运行,一直在发邮件,所以每小时都要通过浏览器手动访问访问令牌link到https://example.com/get_token.php,所以PHPMailer可以使用访问令牌工作。

有没有办法再次获取刷新令牌,或者自动从 link 到 get_token.php 来访问访问令牌?例如:使用命令行,因此 cronJob 将每小时执行一次。

我的 get_token.phpthephpleague/oauth2-client 文档中显示的示例非常相似,但我没有得到 refresh_token

<?
require 'vendor/autoload.php';

session_start();

$token_file="my-token.json";

$provider = new \League\OAuth2\Client\Provider\Google([
    'clientId'                => 'xxx.apps.googleusercontent.com',
    'clientSecret'            => 'password',
    'redirectUri'             => 'https://example.com/get_token.php',
    'scopes' => ['https://mail.google.com/'],
    'access_type' => 'offline'
]);

if (!isset($_GET['code'])) {

    $authorizationUrl = $provider->getAuthorizationUrl();

    $_SESSION['oauth2state'] = $provider->getState();

    header('Location: ' . $authorizationUrl);
    exit;

} elseif (empty($_GET['state']) || (isset($_SESSION['oauth2state']) && $_GET['state'] !== $_SESSION['oauth2state'])) {

    if (isset($_SESSION['oauth2state'])) {
        unset($_SESSION['oauth2state']);
    }

    exit('Invalid state');

} else {

    try {

        $accessToken = $provider->getAccessToken('authorization_code', [
            'code' => $_GET['code']
        ]);

        $token=json_decode(file_get_contents($token_file));
        $token->access_token=$accessToken->getToken();

        if(!empty($accessToken->getRefreshToken())){
            $token->refresh_token=$accessToken->getRefreshToken();
        }

        file_put_contents($token_file, json_encode($token));
        
    } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {

        exit($e->getMessage());

    }

}

这是我的 Mail.php

$token_file="my-token.json";
$mail = new PHPMailer();

try {

    $mail->IsSMTP();
    $mail->SMTPAuth = true;
    $mail->SMTPSecure = "tls";
    $mail->Host = "smtp.gmail.com";
    $mail->Port = 587;
    $mail->AddAddress($_GET['to']);
        
    $oauth_token=file_get_contents($token_file);
    $refresh_token= json_decode($oauth_token)->refresh_token;
                  
    $mail->AuthType ="XOAUTH2";
    $mail->oauthUserEmail = 'auto-mail@example.com';
    $mail->oauthClientId = "xxx.apps.googleusercontent.com";
    $mail->oauthClientSecret = "password";
    $mail->oauthRefreshToken = $refresh_token;

    $mail->CharSet = 'utf-8';
    $mail->SMTPDebug = 2;
    $mail->Encoding   = "base64";

    return $mail->Send();

} catch (Exception $e) {
    return false;
}

First issue: 关闭 我认为您对刷新令牌有一些误解。

如果您查看 expiration 页面,您将设置此

There is currently a limit of 50 refresh tokens per Google Account per OAuth 2.0 client ID. If the limit is reached, creating a new refresh token automatically invalidates the oldest refresh token without warning. This limit does not apply to service accounts.

是的,用户的刷新令牌上限为 50 OUTSTANDING。这意味着如果我 运行 你的应用程序并授权它,我会得到一个刷新令牌。如果我再次 运行 它,我会得到另一个刷新令牌。我可以这样做 50 次,你的应用程序将有 50 个未完成的刷新令牌到我的帐户,它们都可以工作。一旦我做了第 51 次,第一个就会过期。

因此每个用户最多可以拥有 50 个突出的刷新令牌。

Second issue: Google 不会 return 为用户的每个授权请求提供新的刷新令牌。 (不要问我为什么。它似乎是特定于语言的。C# 和 java 每次都会得到一个新的令牌。PHP 和 Python 不会出现to.) Google assumes 您已存储该刷新令牌。我想如果你在请求中做 prompt=consent 它会强制它 return 给你一个新的。您还可以通过 google 帐户让用户撤销您对其应用程序的访问权限,这也会触发新的同意和 return 访问令牌和刷新令牌。

如果 prompt=consent 不起作用,请通过此 link 并从您的 google 帐户中删除应用访问权限。

Third issue: 刷新令牌和访问令牌不可互换。刷新令牌用于请求新的访问令牌。访问令牌用于访问 api.

所以通过这样做

$access_token= json_decode($oauth_token)->access_token;
              
$mail->AuthType ="XOAUTH2";
$mail->oauthUserEmail = 'auto-mail@example.com';
$mail->oauthClientId = "xxx.apps.googleusercontent.com";
$mail->oauthClientSecret = "password";
$mail->oauthRefreshToken = $access_token;

它会失败,因为它会尝试使用访问令牌来刷新访问令牌,但这是行不通的。

$refresh_token= json_decode($oauth_token)->refresh_token;
              
$mail->AuthType ="XOAUTH2";
$mail->oauthUserEmail = 'auto-mail@example.com';
$mail->oauthClientId = "xxx.apps.googleusercontent.com";
$mail->oauthClientSecret = "password";
$mail->oauthRefreshToken = $refresh_token;