Unable to decode credential from JWT - php

I am trying to implement the new "Sign in with Google" button as described in https://developers.google.com/identity/gsi/web/guides/display-button.
Everything is fine, and I am able to get a response from the button with "credential" and "g_csrf_token" elements, which I can send to my server. However, using the Google API Client to decode the credential doesn't work. I'm trying to follow the instructions.
Here's my code:
$id_token = filter_input(INPUT_POST, 'credential');
$csrfToken = filter_input(INPUT_POST, 'g_csrf_token'); //??? Do we need this?
$client = new Google_Client(['client_id' => $clientid]);
$client->addScope("email"); // Recommended in another StackOverflow answer but makes no difference
try {
$payload = $client->verifyIdToken($id_token);
} catch(Exception $ex) {
$errorMessage = "Error in verifyIdToken():" . $ex->getMessage();
// ...do stuff with the error message
}
// ...do stuff with the returned payload
The result is the error message id_token must be passed in or set as part of setAccessToken.
I've updated my Google API Client to v2.11.
I assume that I've missed a step somewhere - can someone help?

Have found a solution, by trial and error! Turns out that $id_token needs to be passed to the client twice, once in setAccessToken() and then again in verifyIdToken(). Omitting setAccessToken fails (like the error message says), but if you pass it in setAccessToken but NOT in verifyIdToken, that doesn't work either.
$id_token = filter_input(INPUT_POST, 'credential');
$client = new Google_Client(['client_id' => $clientid]);
try {
$client->setAccessToken($id_token);
$payload = $client->verifyIdToken($id_token);
} catch(Exception $ex) {
$errorMessage = "Error in verifyIdToken():" . $ex->getMessage();
// ...do stuff with the error message
}
// ...do stuff with the returned payload
It would nice, if you're at Google and picking this up, if you updated the documentation.

Related

How do I authenticate a service account in Google Cloud Services in PHP?

While developing Recaptcha Enterprise for use of the V2 "I am not a robot" checkbox, I am stuck on this error:
Fatal error: Uncaught DomainException: Could not load the default credentials. Browse to https://developers.google.com/accounts/docs/application-default-credentials for more information
I follow the link and have settled on this to authenticate:
use Google\Cloud\Storage\StorageClient;
$storage = new StorageClient([
'keyFile' => json_decode(file_get_contents($path_to_keyfile), true),
'projectId' => 'MY_PROJECT'
]);
I cannot find anything else that suggests I need to do anything more than this, and this link to the constructor API doesn't suggest I can pass it in as a parameter and then proceed. I do not want to use environment variables for this project, I want to connect manually in the code. What am I missing? I can confirm I have a working service account.
If it's helpful, the code I'm trying to run after I presumably authenticate is this:
// ==================== CAPTCHA ===================
use Google\Cloud\RecaptchaEnterprise\V1\RecaptchaEnterpriseServiceClient;
use Google\Cloud\RecaptchaEnterprise\V1\Event;
use Google\Cloud\RecaptchaEnterprise\V1\Assessment;
use Google\Cloud\RecaptchaEnterprise\V1\TokenProperties\InvalidReason;
$captcha_response = $_POST['g-recaptcha-response'];
$site_key = "123456789abc";
$client = new RecaptchaEnterpriseServiceClient();
define('SITE_KEY', $site_key);
define('TOKEN', $captcha_response);
define('PROTECTED_ACTION', 'signup');
define('PARENT_PROJECT', 'projects/MY_PROJECT');
$event = (new Event())
->setSiteKey(SITE_KEY)
->setExpectedAction(PROTECTED_ACTION)
->setToken(TOKEN);
$assessment = (new Assessment())
->setEvent($event);
try {
$response = $client->createAssessment(
PARENT_PROJECT,
$assessment
);
if ($response->getTokenProperties()->getValid() == false) {
printf('The CreateAssessment() call failed because the token was invalid for the following reason: ');
printf(InvalidReason::name($response->getTokenProperties()->getInvalidReason()));
} else {
if ($response->getEvent()->getExpectedAction() == PROTECTED_ACTION) {
printf('The score for the protection action is:');
printf($response->getRiskAnalysis()->getScore());
}
else
{
printf('The action attribute in your reCAPTCHA tag does not match the action you are expecting to score');
}
}
} catch (exception $e) {
printf('CreateAssessment() call failed with the following error: ');
printf($e);
}
Here's how I got it working. Thanks to John Hanley for the help in a previous answer. The documentation had lead me to believe that (for whatever reason) Storage was required, but that was not the case: it was as simple as providing the path to the key via the credentials parameter. Not the keyFile parameter.
if (empty($_POST['g-recaptcha-response']))
die("You have failed the not-a-robot check.");
$captcha_response = $_POST['g-recaptcha-response'];
require 'composer/vendor/autoload.php';
use Google\Cloud\RecaptchaEnterprise\V1\RecaptchaEnterpriseServiceClient;
use Google\Cloud\RecaptchaEnterprise\V1\Event;
use Google\Cloud\RecaptchaEnterprise\V1\Assessment;
use Google\Cloud\RecaptchaEnterprise\V1\TokenProperties\InvalidReason;
$path_to_keyfile = "MY_PROJECT-1234567890abc.json";
$site_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$client = new RecaptchaEnterpriseServiceClient([
'credentials' => $path_to_keyfile,
'projectId' => 'MY_PROJECT'
]);
define('SITE_KEY', $site_key);
define('TOKEN', $captcha_response);
define('PROTECTED_ACTION', 'signup');
define('PARENT_PROJECT', 'projects/MY_PROJECT');
$event = (new Event())
->setSiteKey(SITE_KEY)
->setExpectedAction(PROTECTED_ACTION)
->setToken(TOKEN);
$assessment = (new Assessment())
->setEvent($event);
try {
$response = $client->createAssessment(PARENT_PROJECT, $assessment);
if ($response->getTokenProperties()->getValid() == false) {
printf('The CreateAssessment() call failed because the token was invalid for the following reason: ');
printf(InvalidReason::name($response->getTokenProperties()->getInvalidReason()));
exit;
} else {
if ($response->getEvent()->getExpectedAction() == PROTECTED_ACTION) {
// Closer to 1 = human, to 0 = robot.
$bot_score = $response->getRiskAnalysis()->getScore();
// do what you want with the score here...
} else {
die('The action attribute in your reCAPTCHA tag does not match the action you are expecting to score');
}
}
} catch (exception $e) {
printf('CreateAssessment() call failed with the following error: ');
printf($e);
exit;
}
Your problem is that you are not specifying the service account to use in the client constructor and the system is falling back to using ADC (Application Default Credentials).
ADC will check the environment variable GOOGLE_APPLICATION_CREDENTIALS for the service account JSON key file.
You can set the environment variable before running your program:
Windows:
set GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
Linux:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
Or modify your program by changing this line of code:
$client = new RecaptchaEnterpriseServiceClient();
To this:
$options = ['keyFile' => $path_to_keyfile];
$client = new RecaptchaEnterpriseServiceClient($options);
Note 1:
If you are running your program on a Google Cloud computer service such as Compute Engine, App Engine, Cloud Run, ... the default service account will be used if neither of the above methods are implemented.
Note 2:
While developing, another method is to use the CLI's application default credentials. Run the following command using the Google Cloud SDK CLI:
gcloud auth application-default login
However, I have not verified that the reCAPTCHA Enterprise library checks for this type of credential.

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...

fitbit php auth doesn't work

After 10 hours of trying various fitbit php libraries I'm turning to stackoverflow for help.
This doesn't work: https://github.com/heyitspavel/fitbitphp
Using
$profile = $fitbit->getProfile();
with that library returns
Fatal error: Uncaught exception 'FitBitException' with message 'Your Fitbit request failed. Code: 400' in /var/www/api/fitbitphp.php:324 Stack trace: #0 /var/www/api/addFitbit.php(22): FitBitPHP->getProfile() #1 {main} thrown in /var/www/api/fitbitphp.php on line 324
This the library on the fitbit website, seems like a lot of people have a problem with this.
public function getProfile()
{
$headers = $this->getHeaders();
try {
$this->oauth->fetch($this->baseApiUrl . "user/" . $this->userId . "/profile." . $this->responseFormat, null, OAUTH_HTTP_METHOD_GET, $headers);
} catch (Exception $E) {
}
$response = $this->oauth->getLastResponse();
$responseInfo = $this->oauth->getLastResponseInfo();
if (!strcmp($responseInfo['http_code'], '200')) {
$response = $this->parseResponse($response);
if ($response)
return $response;
else
throw new FitBitException($responseInfo['http_code'], 'Fitbit request failed. Code: ' . $responseInfo['http_code']);
} else {
throw new FitBitException($responseInfo['http_code'], 'Your Fitbit request failed. Code: ' . $responseInfo['http_code']);
}
}
I tried this here as well but it doesn't return the user token or session id https://github.com/nostra999/fitbit-api
Perhaps missed out the init step, as described in the lib README file (https://github.com/heyitspavel/fitbitphp/blob/master/README.md)
Simple working usage is:
<?php
define('FITBIT_KEY', '777'); // The application key registered
define('FITBIT_SECRET', '777'); // The application secret registered
$fitbit = new FitBitPHP(FITBIT_KEY, FITBIT_SECRET);
$fitbit->initSession('http://localhost:8080/fibit'); // callback URL
$fitbit->getProfile();
Also from the Fitbit API Documentation:
https://wiki.fitbit.com/display/API/API+Response+Format+And+Errors#APIResponseFormatAndErrors-Response
400 Bad Request Any case where either endpoint doesn't exist, resource
path parameters are invalid, POST request parameters are invalid or no
Authentication header provided. This doesn't include invalid specific
resource ids
If this does not help, please provide the full code that you run, not just
$profile = $fitbit->getProfile();

Facebook Long Token: you must provide or set a default application secret

Im using Facebook PHP SDK to exchange a short lived token for a new long lived token.
As it says here, you should just call a URL with these parameters:
GET /oauth/access_token?
client_id={app-id}
&client_secret={app-secret}
&grant_type=client_credentials
If I call this directly in browser, it works ok. I could just do a curl call and that would be ok. But I want stay close the oficial FacebookSDK, so in my Class I did this method:
public function renewToken($userShortToken, $redirectURI = FALSE) {
if ($redirectURI !== FALSE)
$this->redirectURI = $redirectURI;
$this->fbSession = new FacebookSession($userShortToken);
$params = '/oauth/access_token?grant_type=fb_exchange_token' .
'&client_id=' . $this->appId .
'&client_secret=' . $this->appSecret .
'&fb_exchange_token=' . $userShortToken .
'&appsecret_proof=' . hash_hmac('sha256', $userShortToken, $this->appSecret);
# About appsecret_proof: https://developers.facebook.com/docs/graph-api/securing-requests
$this->debug->log('params', $params);
try {
$request = new FacebookRequest($this->fbSession, 'GET', $params);
$this->debug->log('request', $params);
$response = $request->execute();
$this->debug->log('response', $response);
$object = $response->getGraphObject();
return $object;
} catch (FacebookRequestException $ex) {
$this->debug->log('FacebookRequestException', $ex);
} catch (\Exception $ex) {
$this->debug->log('FacebookRequestException', $ex->getMessage());
}
return FALSE;
}
PS: the "$userShortToken" is sent via Ajax to the server, after the user has logged in my website through Facebook Javascritp SDK.
So I see in my console these errors (im using FirePHP to output messages to console):
/oauth/access_token?grant_type=fb_exchange_token&client_id=305...57&client_secret=759...6c4&fb_exchange_token=CAAEV...lR&appsecret_proof=2e...76
You must provide or set a default application secret. FacebookRequestException /myserver/.../socialshare/MyClass.php:96
I cant understanding this message. I setted the app secret, the short token, all the necessary parameters in the URL. Even the "appsecret_proof" hash. It is all correct. If I copy the url and call the graph, it works and give me back the long Token.
Does someone have an idea how to stick with FacebookSDK or I should just drop that for this task? I just want to do this in the most correct possible way.
Thank you.
When I get this error it helped me to call
FacebookSession::setDefaultApplication($fbAppId, $fbAppSecret);
before creating a FacebookSession

Can't get soundcloud access_token using php sdk. invalid_grant error

I am using soundcloud php sdk. It works successfully when i use the sdk to get track. But when i've tried to get an access Token in order to use it, the sdk send me back an error 401. After debugging the error message, i ve get the responseJSON below: {"error":"invalid_grant"}
This is my code , i m using Zend framework
$code = $this->getRequest()->getParam('code',false);
if($code){
$client = new Services_Soundcloud($this->soundcloud['client_id'],$this->soundcloud['secret_key']);
try {
$access_token = $client->accessToken($code);
} catch (Services_Soundcloud_Invalid_Http_Response_Code_Exception $e) {
exit($e->getMessage());
}
}
Did anyone has any idea about how to solve this error ?
In order to exchange a code for an access token, you must instantiate your Services_Soundcloud instance with a client id, client secret and a redirect uri. The following code should work, assuming you are storing the redirect uri in $this->soundcloud:
$client = new Services_Soundcloud(
$this->soundcloud['client_id'],
$this->soundcloud['secret_key'],
$this->soundcloud['redirect_uri']
);
$code = $this->getRequest()->getParam('code',false);
if ($code) {
try {
$access_token = $client->accessToken($code);
} catch (Services_Soundcloud_Invalid_Http_Response_Code_Exception $e) {
exit($e->getMessage());
}
}
Let me know if that solves the problem.

Categories