I have the following code:
if (isset($_REQUEST['logout']))
{
unset($_SESSION['upload_token ']);
}
if (isset($_GET['code']))
{
$client->authenticate($_GET['code']);
$_SESSION['upload_token'] = $client->getAccessToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
}
if (isset($_SESSION['upload_token']) && $_SESSION['upload_token'])
{
$client->setAccessToken($_SESSION['upload_token']);
if ($client->isAccessTokenExpired())
{
echo "The access token is expired.<br>"; // Debug
$client->refreshToken(json_decode($_SESSION['upload_token']));
unset($_SESSION['upload_token']);
}
}
else
{
$authUrl = $client->createAuthUrl();
}
and am receiving the following error:
Uncaught exception 'Google_Auth_Exception' with message '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.'
I am assuming I am getting this error because the response was auto-approved.
What should be changed?
UPDATE: I've tried adding this to my code:
$client->setAccessType("online");
$client->setApprovalPrompt("auto");
Based on this question. I still am receiving the same error of missing refresh token.
UPDATE: After kroikie's update, my code looks like the following:
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope("https://www.googleapis.com/auth/drive");
$client->setAccessType("offline");
$client->setApprovalPrompt("auto");
$client->setApplicationName("Appraisal App");
$service = new Google_Service_Drive($client);
if (isset($_REQUEST['logout']))
{
unset($_SESSION['upload_token ']);
}
if (isset($_GET['code']))
{
$resp = $client->authenticate($_GET['code']);
$_SESSION['upload_token'] = $client->getAccessToken();
$array = get_object_vars(json_decode($resp));
// store and use $refreshToken to get new access tokens
$refreshToken = $array['refreshToken'];
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
}
if (isset($_SESSION['upload_token']) && $_SESSION['upload_token'])
{
$client->setAccessToken($_SESSION['upload_token']);
if ($client->isAccessTokenExpired())
{
echo "The access token is expired. Let Raph know that you saw this.<br>";
$client->refreshToken($refreshToken);
unset($_SESSION['upload_token']);
}
}
else
{
$authUrl = $client->createAuthUrl();
}
Unfortunately, I still receive the same fatal error when the refresh token is needed.
When the access type is offline, an access token and a refresh token are returned when the user first grants data access. The access token can be used to access the user's data, and the refresh token is stored and used to get a new access token when the initial access token has expired.
So try using offline access type
$client->setAccessType('offline');
and use the refresh token to refresh the client's access token
// $refreshToken is retrieved from the response of the
// user's initial granting access
$client->refreshToken($refreshToken)
UPDATE:
To get the refresh token use something like:
if (isset($_GET['code'])) {
$resp = $client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
$array = get_object_vars(json_decode($resp));
// store and use $refreshToken to get new access tokens
$refreshToken = $array['refreshToken'];
}
Assuming you want to use the offline access type, you have two choices: either you force the approval prompt (so you never get an auto-approved response):
$client->setAccessType("offline");
$client->setApprovalPrompt("force");
Or you ignore the refresh token and simply get a new auto-approved authorization when your current token is expired. I never tested this but since no user action is required for approval, I assume it works.
You can read more about this here (Change #3: Server-side auto-approval): http://googlecode.blogspot.ca/2011/10/upcoming-changes-to-oauth-20-endpoint.html
Sometimes it's seems that the error is in the code, but in my case was not.
I had the same problem and tried all kind of solution, but the error was in the console.developers.google.com configuration.
I thought was not necessary to confirm the domain in the Credentials configuration.
So when i did it, so the code stopped giving the error.
Related
Till now i am using short-lived token and refresh token for API auth. I am using refresh token only for getting user-id to query database to check latest permissions and active/blocked status of user. Now i am thinking that why should not i extract this user-id from short-liven token itself. The following function is used to decode JWT, in this expiration is verified after signature verification. So if i get 'expiration' error it means then token signature is good and token is un-tempered. Now i can extract middle(yyy out of xxx.yyy.zzz) base64 encoded data from expired JWT to get user-id. So i don't seeing worth using refresh token. Further longer time access can also be defined in token itself with just custom timestamp so that i have both time limits in one token for example 5 minutes and 90 days. What are your thoughts?
public static function decode($jwt, $key, array $allowed_algs = array())
{
$timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;
if (empty($key)) {
throw new InvalidArgumentException('Key may not be empty');
}
$tks = explode('.', $jwt);
if (count($tks) != 3) {
throw new UnexpectedValueException('Wrong number of segments');
}
list($headb64, $bodyb64, $cryptob64) = $tks;
if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
throw new UnexpectedValueException('Invalid header encoding');
}
if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
throw new UnexpectedValueException('Invalid claims encoding');
}
if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
throw new UnexpectedValueException('Invalid signature encoding');
}
if (empty($header->alg)) {
throw new UnexpectedValueException('Empty algorithm');
}
if (empty(static::$supported_algs[$header->alg])) {
throw new UnexpectedValueException('Algorithm not supported');
}
if (!in_array($header->alg, $allowed_algs)) {
throw new UnexpectedValueException('Algorithm not allowed');
}
if (is_array($key) || $key instanceof \ArrayAccess) {
if (isset($header->kid)) {
if (!isset($key[$header->kid])) {
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
}
$key = $key[$header->kid];
} else {
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
}
}
// Check the signature
if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
throw new SignatureInvalidException('Signature verification failed');
}
// Check the nbf if it is defined. This is the time that the
// token can actually be used. If it's not yet that time, abort.
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
);
}
// Check that this token has been created before 'now'. This prevents
// using tokens that have been created for later use (and haven't
// correctly used the nbf claim).
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
);
}
// Check if this token has expired.
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
throw new ExpiredException('Expired token');
}
return $payload;
}
Don't do this. Your plan breaks the common practice. It is also against the OAuth standard. New developer will think it is a bug. Your company might hire security auditor and that person will test access with expired token. This will be reported as something you have to fix.
I'm trying to use Google Oauth to access Google Analytics Datas.
It's works fine except with token. The token expires after an hour and I don't know how to refresh it. There a line where there's "For simplicity of the example we only store the accessToken. If it expires use the refreshToken to get a fresh one" but I don't know how to…
Here's my code
$client_id = 'xxxxxxxxxx.apps.googleusercontent.com';
// From the APIs console
$client_secret = 'xxxxxxxxxxxxx';
// Url to your this page, must match the one in the APIs console
$redirect_uri = 'mylocalurl.php';
session_start();
include('GoogleAnalyticsAPI.class.php');
$ga = new GoogleAnalyticsAPI();
$ga->auth->setClientId($client_id);
$ga->auth->setClientSecret($client_secret);
$ga->auth->setRedirectUri($redirect_uri);
if (isset($_GET['force_oauth'])) {
$_SESSION['oauth_access_token'] = null;
}
/*
* Step 1: Check if we have an oAuth access token in our session
* If we've got $_GET['code'], move to the next step
*/
if (!isset($_SESSION['oauth_access_token']) && !isset($_GET['code'])) {
// Go get the url of the authentication page, redirect the client and go get that token!
$url = $ga->auth->buildAuthUrl();
header("Location: ".$url);
}
/*
* Step 2: Returning from the Google oAuth page, the access token should be in $_GET['code']
*/
if (!isset($_SESSION['oauth_access_token']) && isset($_GET['code'])) {
$auth = $ga->auth->getAccessToken($_GET['code']);
if ($auth['http_code'] == 200) {
$accessToken = $auth['access_token'];
$refreshToken = $auth['refresh_token'];
$tokenExpires = $auth['expires_in'];
$tokenCreated = time();
// For simplicity of the example we only store the accessToken
// If it expires use the refreshToken to get a fresh one
$_SESSION['oauth_access_token'] = $accessToken;
} else {
die("Sorry, something wend wrong retrieving the oAuth tokens");
}
}
Thanks
I am not sure of the details of doing this in PHP but there is an end point to request against for refreshing the access token.
The API end point is https://accounts.google.com/o/oauth2/token and the body of request should be something like
{
'refresh_token' => your_stored_refresh_token,
'client_id' => ENV['CLIENT_ID'],
'client_secret' => ENV['CLIENT_SECRET'],
'grant_type' => 'refresh_token'
}
If successful that request will return a fresh access token.
I'm using the artdarek package to implement the login process in my web application.
I get a weird behavior by using the twitter login buton action; The application always asks for permission, although the user have accepted it already. Does twitter applications asks for the permission every time we want to login using twitter?
I want to mention that I activated the read,write permission in the settings of my twitter apps.
The action of the twitter login is:
public function loginWithTwitter() {
// get data from input
$token = Input::get( 'oauth_token' );
$verify = Input::get( 'oauth_verifier' );
// get twitter service
$tw = OAuth::consumer( 'Twitter' );
// check if code is valid
// if code is provided get user data and sign in
if ( !empty( $token ) && !empty( $verify ) ) {
// This was a callback request from twitter, get the token
$token = $tw->requestAccessToken( $token, $verify );
// Send a request with it
$result = json_decode( $tw->request( 'account/verify_credentials.json' ), true );
// return $result['source'];
// $message = 'Your unique Twitter user id is: ' . $result['id'] . ' and your name is ' . $result['name'];
//echo $message. "<br/>";
//Var_dump
//display whole array().
dd($result);
// var_dump($result);
return Redirect::route('login');
}
}
// if not ask for permission first
else {
// get request token
$reqToken = $tw->requestRequestToken();
// get Authorization Uri sending the request token
$url = $tw->getAuthorizationUri(array('oauth_token' => $reqToken->getRequestToken()));
// return to twitter login url
return Redirect::to( (string)$url );
}
}
Is there any way to check wether the application was already enabled in twitter so that user will not pass by the permission window every time he login using twitter?
Here I need to automatically post on Twitter after authentication.
My code
<?php
session_start();
include("twitteroauth.php");
define('CONSUMER_KEY','zubLdCze6Erz9SVNIgG3w');
define('CONSUMER_SECRET','ZnZQ77bNnpBER2aSxTQGEXToAQODz9qEBSAXIdeYw');
define('OAUTH_CALLBACK','http://dev.pubs.positive-dedicated.net/Licensee/addPubEventWeekly.php');
/* Build TwitterOAuth object with client credentials. */
$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET);
/* Get temporary credentials. */
$request_token = $connection->getRequestToken(OAUTH_CALLBACK);
/* Save temporary credentials to session. */
$_SESSION['oauth_token'] = $token = $request_token['oauth_token'];
$_SESSION['oauth_token_secret'] = $request_token['oauth_token_secret'];
switch ($connection->http_code) {
case 200:
/* Build authorize URL and redirect user to Twitter. */
$url = $connection->getAuthorizeURL($token);
header('Location: ' . $url);
break;
default:
/* Show notification if something went wrong. */
echo 'Could not connect to Twitter. Refresh the page or try again later.';
}
?>
and
ksort($_SESSION);
$tweet = new TwitterOAuth('zubLdCze6Erz9SVNIgG3w','ZnZQ77bNnpBER2aSxTQGEXToAQODz9qEBSAXIdeYw',$_SESSION['oauth_token'],$_SESSION['oauth_token_secret']);
print_r($tweet);
$account = $tweet->get('account/verify_credentials');
$message = "This is an example twitter post using PHP";
$postVal = $tweet->post('statuses/update', array('status' => $message));
But here I am getting "Invalid/Expired access token" Error.
I have changed twittreauth.php library file also from 1.0 to 1.1 from here
public $host = "https://api.twitter.com/1.1/";
Please suggest how to fix this
I'm storing the oauth info from Twitter in a Flash Cookie after the user goes though the oauth process. Twitter says that this token should only expire if Twitter or the user revokes the app's access.
Is there a call I can make to Twitter to verify that my stored token has not been revoked?
All API methods that require authentication will fail if the access token expires. However the specific method to verify who the user is and that the access token is still valid is GET account/verify_credentials
This question may be old, but this one is for the googlers (like myself).
Here is the call to twitter using Hammock:
RestClient rc = new RestClient {Method = WebMethod.Get};
RestRequest rr = new RestRequest();
rr.Path = "https://api.twitter.com/1/account/verify_credentials.json";
rc.Credentials = new OAuthCredentials
{
ConsumerKey = /* put your key here */,
ConsumerSecret = /* put your secret here */,
Token = /* user access token */,
TokenSecret = /* user access secret */,
Type = OAuthType.AccessToken
};
rc.BeginRequest(rr, IsTokenValid);
Here is the response:
public void IsTokenValid(RestRequest request, RestResponse response, object userState)
{
if(response.StatusCode == HttpStatusCode.OK)
{
var user = userState;
Helper.SaveSetting(Constants.TwitterAccess, user);
}
else
{
Dispatcher.BeginInvoke(() => MessageBox.Show("This application is no longer authenticated "))
}
}
I always borrow solutions from SO, this is my first attempt at giving back, albeit quite late to the question.
When debugging manually:
curl \
--insecure https://api.twitter.com/1/account/verify_credentials.json?oauth_access_token=YOUR_TOKEN
I am using TwitterOAuth API and here is the code based on the accepted answer.
$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $twitter_oauth_token, $twitter_oauth_secret);
$content = $connection->get("account/verify_credentials");
if($connection->getLastHttpCode() == 200):
// Connection works fine.
else:
// Not working
endif;