Wrong number of segments in token (OAuth Google Api with php) - php

I have a php application that use OAuth2 to authenticate users to their account.
until yesterday, everything worked very well.
But today, and without changing my code, when I try to access to my account, and after I authenticates to my google account, I obtain a blank page.
I debug the code and I found that it crashed when the Google_Client try to verifyIdToken and more exactly in the function verifySignedJwtWithCerts because : $segments = explode(".", $jwt); find 4 segments and not 3.
here is my code :
...
$client = new Google_Client();
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);
$client->setRedirectUri($redirectUri);
$client->setScopes("email");
if(!isset($_GET['code']))
header("Location: ".$client->createAuthUrl());
else
{
$client->authenticate($_GET['code']);
$_SESSION["access_token"] = $client->getAccessToken();
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
} else {
$authUrl = $client->createAuthUrl();
}
$ticket = $client->verifyIdToken();
if ($ticket) {
$admin = $ticket->getAttributes();
}
$_SESSION["email"] = $admin["payload"]["email"];
...
Can anyone offer an explanation?
Thank's in advance

Some how I didn't find any answer in Google. So I going to share what I do to make it work.
The short answer: Because the token you use in verifyIdToken() is invalid.
The long answer as below:
I'm not sure if it is the correct steps:
After Google Login Authentication, we will get the "Authorization
Code" from oauthplayground (for testing). Please enter your oauth client_id and oauth client secret in the setting before usage.
I have use the "Authorization Code" to get the other tokens by writing
$token = $client->fetchAccessTokenWithAuthCode("THE_AUTHORIZATION_CODE");
Inside $token, I have receive the following: access_token, token_type, expires_in, refresh_token, id_token, created.
Now use the id_token in $payload = $client->verifyIdToken(id_token);
Then you will get the correct information you needed like names, aud, exp, iss etc in $payload.
Note: To avoid your code return the "Wrong number of segments in token" error again, you have to try and catch the verifyIdToken method
try {
$payload = $client->verifyIdToken(id_token);
}
catch (Exception $e) {
echo "Invalid id token";
}

On my side, I've been working on MERN Application and, the issue was on the way I send the tokenId on the backend.
Check if the idToken you are sending to the backend is the same as the one you are getting on googleSuccess.
before.
const onGoogleSuccess = (response) => {
const tokenId = response.accessToken;//Wrong
console.log("SUCCESS::", response);
dispatch(login({ tokenId })).then((res) => {
console.log("GOOGLE-LOGIN::", res);
});
};
After:
before.
const onGoogleSuccess = (response) => {
const tokenId = response.accessToken;
console.log("SUCCESS::", response);
dispatch(login({ tokenId })).then((res) => {
console.log("GOOGLE-LOGIN::", res);
});
};

Related

Oauth2 apple signin on php laravel

I'm still a newbie and want to ask about the task I'm working on,
in this case I'm making authentication using oauth2 to be able to access apple api,
but i have an error when signin using apple, invalid_client appears,
for invalid_client error, I've done the following way:
check the client_id used in the http request (doubts) (i've try by changing client_id)
adjust the jwt header and jwt payload to the parameters requirements (checked)
and check the JWT signature (checked)
of the three steps that I tried, I felt doubt in the script authentication or client id
please tell me where my fault is in the client_id naming or in the script section
thank you for the help, sorry for my bad english here I include the script that I use
``
$provider = new Apple([
'clientId' => 'com.example.example',
'teamId' => 'apple-team-id',
'keyFileId' => 'apple-key-file-id',
'keyFilePath' => storage_path('apple-key-file-path'),
'redirectUri' => 'http://localhost:8000/Store/Gold_Store/GoldStore-create',
]);
if(!isset($_POST['code'])){
//jika kita tidak mempunyai authorization code
$authUrl = $provider->getAuthorizationUrl();
$_SESSION['oauth2state'] = $provider->getState();
header('Location: '.$authUrl);
exit;
//check given state against previously stored one to mitigate CSRF attack
} elseif (empty($_POST['state']) || ($_POST['state'] !== $_SESSION['oauth2state'])) {
unset($_SESSION['oauth2state']);
exit('Invalid state');
}else{
//try to get access token(using the authorization code grant)
$token = $provider->getAccessToken('authorization_code', [
'code' => $_POST['code']
]);
//Optional: Now u have a token u can look up a user profile data
try {
//we got an access token, lets now get the user's detail
$user = $provider->getResourceOwner($token);
//use these details to create a new profile
printf('hello %s!', $user->getFirstName());
//refresh token
$refreshToken = $token->getRefreshToken();
$refreshTokenExpiration = $token->getRefreshTokenExpires();
} catch (Exception $e) {
//Failed to get user details
exit(':-(');
}
//use this to interact with an API on the users behalf
echo $token->getToken();
}
this is my json result
``{
"error": "invalid_client"
}
invalid_client error mean the client secret is wrong, maybe the issue in how you generate it or the details which you used.
You can check this snipped of code to test PHP apple sign in
https://gist.github.com/ameen-sarsour/e14a1d5bae5b61080dfdd5b1430c3e10

Refresh token on Google OAuth2 PHP library

I implemented Google OAuth2 for user login on my website.
It works but after 1 hour token expires and login fails.
I read on the web that I need to get the Refresh Token (for Facebook Login I used a Long Lived Token), but code I tried doesn't work.
Here the code:
//LOGIN CALLBACK FROM GOOGLE
$gClient = new Google_Client();
$gClient->setApplicationName(SITE_TITLE);
$gClient->setClientId(get_option('google_api_id')->value);
$gClient->setClientSecret(get_option('google_api_secret')->value);
$gClient->addScope('profile');
$gClient->addScope('email');
$gClient->setRedirectUri(SITE_URL."/login/google/google-callback.php");
if(isset($_GET['code'])) {
$gClient->setAccessType('offline');
$token = $gClient->fetchAccessTokenWithAuthCode($_GET['code']);
$gClient->setAccessToken($token['access_token']);
$_SESSION['google_access_token'] = $token['access_token'];
}
if($gClient->getAccessToken()) {
// Get user profile data from google
$google_oauthV2 = new Google_Service_Oauth2($gClient);
$gpUserProfile = $google_oauthV2->userinfo->get();
}
...
This first snippet works fine.
In this second snipped, when user change page, I verify if login is still active:
$gClient = new Google_Client();
$gClient->setApplicationName(SITE_TITLE);
$gClient->setClientId(get_option('google_api_id')->value);
$gClient->setClientSecret(get_option('google_api_secret')->value);
$gClient->addScope('profile');
$gClient->addScope('email');
$gClient->setAccessType('offline');
$gClient->setAccessToken($_SESSION['google_access_token']);
if($gClient->getAccessToken()) {
if ($gClient->isAccessTokenExpired()) {
$gClient->fetchAccessTokenWithRefreshToken($gClient->getRefreshToken());
}
$google_oauthV2 = new Google_Service_Oauth2($gClient);
$gpUserProfile = $google_oauthV2->userinfo->get();
...
}
This second snipped doesn't work, because method fetchAccessTokenWithRefreshToken($gClient->getRefreshToken()) fails because $gClient->getRefreshToken() is NULL.
I debugged the callback and I saw that $token = $gClient->fetchAccessTokenWithAuthCode returns an array without "refresh_token" field.
Can anyone help me?
Thanks
Bye

Google Events API returning 401 error unauthorized_client

I have created an application in laravel to sync my users calendar in my database with incremental sync every thing working perfect when i am running it in my browser.
but its returning 401 unauthorized_client in my cron 100% same code implemented in both files.
$client = $this->getGoogleClient();
$client->setAccessToken($accessToken);
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
}
$gCalService = new Google_Service_Calendar($client);
$optArr = array();
$calendarId = 'primary';
$pageToken = NULL;
$responceEvents = array();
$synced = $emailSync->getGoogleSynced();
if($synced != null)
$optArr['syncToken'] = $synced->sync_token;
do {
if ($pageToken) {
$optArr['pageToken'] = $pageToken;
}
try {
$results = $gCalService->events->listEvents($calendarId,$optArr);
}
catch (Google_Service_Exception $e) {
$msg = $e->getMessage();
return $msg;
}
if ($results->getItems()) {
$responceEvents = array_merge($responceEvents, $results->getItems());
$pageToken = $results->getNextPageToken();
}
} while ($pageToken);
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
returning below error
Array
(
[error] => unauthorized_client
[error_description] => Unauthorized
)
debugged code line by line $client->getRefreshToken() also contain value which i am passing here $client->setAccessToken($accessToken); exactly same code working fine in my browser file and not working in my cron.
401 unauthorized_client
Means that your code has not been authorized. If you want to access private user data you will need to be logged in. Your code needs an access token in order to access the Google api.
What you should be doing is authorizing your code once using Oauth2 in a browser save the refresh token someplace and have the cron job read the refresh token and request a new access token before it runs.
Assuming that you own the account you are trying to insert into you may also want to consider using a service account.
Refresh token
If you are storing the refresh token in the database you must assign it to your client first before running your code here i am doing that with a refresh token stored in a session variable.
$client->refreshToken($_SESSION['refresh_token']);

PHP Gmail API Invalid Grant: Code was already redeemed (Codeigniter)

First of all I want to say that I read all the other post about this same problem and I couldn't find a solution.
This is the error:
exception 'Google_Auth_Exception' with message 'Error fetching OAuth2 access token, message: 'invalid_grant: Code was already redeemed.
In localhost, it works perfect. I can debug it and I get Google plus && emails info without problem.
I create a new credential for production (Web App) and I set my oauth return uri in: Authorized redirect URI
(http://www.__myweb_.com/plus_gmail/oauth2callback)
It works, because I can accept and I get the code in callback, but it fails when I try to authenticate;
I also tried to revokeToken and my Session is empty...
How can I solve this problem??
Thanks a lot!!
This is my code:
function __construct()
{
define('CLIENT_SECRET_PATH', 'client_secret_online.json');
$this->redirect_url=base_url()."plus_gmail/oauth2callback";
$this->client = new Google_Client();
$this->client->setIncludeGrantedScopes(true);
$this->client->setAuthConfigFile(CLIENT_SECRET_PATH);
$this->client->addScope(Google_Service_Plus::PLUS_LOGIN);
$this->client->addScope(Google_Service_Plus::PLUS_ME);
$this->client->addScope(Google_Service_Plus::USERINFO_EMAIL);
$this->client->addScope(Google_Service_Plus::USERINFO_PROFILE);
$this->client->addScope(Google_Service_Gmail::MAIL_GOOGLE_COM);
$this->client->setRedirectUri($this->redirect_url);
$this->client->setAccessType('offline');
$this->client->setIncludeGrantedScopes(true);
}
function index()
{
$auth_url = $this->client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
}
function oauth2callback()
{
if (! isset($_GET['code']))
{
echo "Error getting code"
}
else
{
$token=$_GET['code'];
$data["token"]=$token;
try
{
$this->client->authenticate($token);
$access_token = $this->client->getAccessToken();
$plus = new Google_Service_Plus($this->client);
$me=$plus->people->get('me');
$this->daouser->newUserPlus($me,$access_token);
var_dump($me);
$this->readEmailsId($this->client);
}
catch (Exception $e)
{
var_dump($e)
}
}
}
I also need to know why it shows me the var_dump($e), but I have a new row in my database withe user google plus info.
I delete access to my app in my gmail account (https://security.google.com/settings/security/permissions) and give access again getting the same error (Code was already redeemed). I used a new gmail account and I get also the same error...

How can I get an updated access token, using stored refresh token

I'm building an application that allows the admin to authenticate access to their analytics account for offline usage, and stores the refresh token in the database.
Now when I try to use the API on the frontend, it returns the following error:
"Access Token Expired. There wan a general error : The OAuth 2.0 access token has expired, and a refresh token is not available. Refresh tokens are not returned for responses that were auto-approved."
Here's my code that generates this error so far:
require_once "lib/google/Google_Client.php";
require_once "lib/google/contrib/Google_AnalyticsService.php";
$_analytics = new analytics();
$_googleClient = new Google_Client();
$_googleClient->setClientId($_analytics->gaClientId);
$_googleClient->setClientSecret($_analytics->gaClientSecret);
$_googleClient->setRedirectUri($_analytics->gaRedirectUri);
$_googleClient->setScopes($_analytics->gaScope);
$_googleClient->setAccessType($_analytics->gaAccessType);
// Returns last access token from the database (this works)
$_tokenArray['access_token'] = $_analytics->dbAccessToken($_agencyId);
$_googleClient->setAccessToken(json_encode($_tokenArray));
if($_googleClient->isAccessTokenExpired()) {
// Don't think this is required for Analytics API V3
//$_googleClient->refreshToken($_analytics->dbRefreshToken($_agencyId));
echo 'Access Token Expired'; // Debug
}
if (!$_googleClient->getAccessToken()) {
echo '<h2>Error - Admin has not setup analytics correct yet</h2>';
}
I'm after a function to run something like setRefreshToken - entering the value from the database, from the admin previously authenticating it online.
You could try the following, you would need to add in the code to store the new token in your database.
if($_googleClient->isAccessTokenExpired()) {
// Don't think this is required for Analytics API V3
//$_googleClient->refreshToken($_analytics->dbRefreshToken($_agencyId));
echo 'Access Token Expired'; // Debug
$_googleClient->authenticate();
$NewAccessToken = json_decode($_googleClient->getAccessToken());
$_googleClient->refreshToken($NewAccessToken->refresh_token);
}
Use a try/catch and use the catch for redirection/refresh of the access token.
Below is the solution I used for similar problem:
$plus = new Google_Service_Plus($client);
try {
$me = $plus->people->get('me');
} catch(Exception $e){
if(!(strpos($_SERVER["REQUEST_URI"],'logout'))){
if (isset($authUrl)){
$redirect = $authUrl;
}
else{
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] .'?logout';
}
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
exit;
}
You can replace the statement under try with the line throwing the exception, I guess that would be
$_googleClient->setClientId($_analytics->gaClientId);
or you can also try refreshing the token as per solution given here:
https://stackoverflow.com/a/22096740/1675384

Categories