Reputation: 711
Finally, I get the refresh_token, by revoking from google account(aka remove the apps access through account).
Everything goes normal again, and I don't need to solve the problem as the title 'Get Google access token not through the browser'.
But why I success this time? Because this time I use curl
but not thephpleague/oauth2-client
way to request auth. I think there must missing some parms when I request auth via thephpleague/oauth2-client
.
Here is a curl
way that I get the refresh_token EVERYTIME after I revoke/remove the apps access from account.
<?
//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
}
In short, how to get google OAuth access token automatically not through the browser(that is header('Location: ')
)?
I cannot get the refresh token any more, because I exceed the access limit while developing, and I've tried many ways and still cannot get the refresh token.
I've successfully set up get_token.php
to get Google OAuth2 only for my website(example.com) for PHPMailer, and I can get access token via browser(by select my google account to log in to access).
Because I have exceed the limit of refresh token via development, so I've tried to use another google account to create api for my website(example.com), revoke from google account, delete OAuth and create another one(so there is a new clientId); but none of them work, and I think that I will never get a refresh token again.
Because my website is running and keep sending emails, so every hour I have to manually access the access token by link to https://example.com/get_token.php through the browser, so the PHPMailer can use the access token to work.
Is there a way to get refresh token again, or automatically link to get_token.php
to access the access token?ex: use command line, so the cronJob will do it on every hour.
My get_token.php
is very similar to the example shows on the document of thephpleague/oauth2-client
but I don't get the 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());
}
}
Here is my 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;
}
Upvotes: 2
Views: 1572
Reputation: 117291
First issue:
off I think you have miss understood something about refresh tokens.
If you check the expiration page you will set this
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.
Yes there is a limit of 50 OUTSTANDING refresh tokens for a user. This means if I run your app and authorize it i get a refresh token back. If i run it again i get another refresh token back. I can do this 50 times and your app will have 50 outstanding refresh tokens to my account and they all will work. Once i do it the 51st time the first one will be expired.
So you can have a max of 50 out standing refresh tokens per user.
Second issue:
Google does not return a new refresh token to you with every authorization request for a user. (Don't ask me why. It appears to be language specific. C# and java get a new token back every time. PHP and Python don't appear to.) Google assumes
that you have stored that refresh token. I think if you do prompt=consent
in the requested it will force it to return to you a new one. You can also have the user revoke your access to their app though there google account this would also trigger a new consent and return an access token and refresh token.
If prompt=consent doesn't work then go though this link and remove the apps access from your google account.
Third issue:
Refresh tokens and access tokens are not interchangeable. A refresh token is used to request a new access token. an access token is used to access an api.
So by doing this
$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;
Its going to fail because it will try to use an access token to refresh the access token which will not work.
$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;
Upvotes: 3