Facebook access tokens format - facebook

I am manually building a login system to my application using facebook. Following this tutorial I am told to do some get requests to obtain user information. Echoing an returned token I obtain this
(using FILE_GET_CONTENTS() ):
$access_token = "graph.facebook.com/oauth/access_token?client_id=$client_id&redirect_uri=$redirect_uri&client_secret=$client_secret&code=$user_code") :
echoing $access_token:
access_token = CAAKlMMCiHp(...)ZByh3faLzh &expires=5125282
or this for an app_token ("graph.facebook.com/oauth/access_token?client_id=$client_id&client_secret=$client_secret&grant_type=client_credentials") :
echoing $app_token:
access_token = 744(...)90|vZU2(...)e0-s
The last one to obtain the user information that I have to do is this:
$last_one = "graph.facebook.com/debug_token?access_token=$access_token&app_token=$app_token";
Check above that I have to use $access_token and app_token. The problem is that when I echo $access_token what I obtain is literally access_token = xxxx..., including the "access_token" and the "=" sign instead of just the token.
So, when I try to do my last request ($last_one) I have (with variable replacement):
$last_one = "graph.facebook.com/debug_token?access_token=access_token=xxx&app_token=app_token=xxxx";
To avoid this I removed the first "access_token=" and "app_token=" and successfully eliminated the duplicate. Unfortunately I now have access_token=xxx&access_token=xxx because although one is literally for access and another is to the app both are treated as access_token and their echo also explicits "access_token =" as shown above.
My question is if this is supposed to happen and how can I get only the token? I find it kind of ridiculous to have the need of using substr() just to eliminate the first part of the token "access_token =". Am I supposed to reach the token by a getter part of file_get_contents()? And should I include &expires=xxxx of the first token in the last request? Please refer and shortly read the tutorial from "Confirming identities" to understand what is my dilema. I hope I made myself through, thank you very much!

Related

Azure Data Factory/SQL and Deputy Payroll API Connection - Anonymous connection possible?

First question - is it possible to access Deputy Payroll API with only the permanent/anonymous token? If not, probably don't need to read the rest.
I have been tasked with moving data from a Deputy Payroll API to an Azure SQL DW.
I am trying to accomplish this without OAuth2.0 or Azure/Deputy Integration
According to Deputy's documentation you can connect via Permanent Token.
I have used permanent tokens before and REST but I am getting different behaviour.
When I ping the Subdomain for the base deputy URL (https://business.na.deputy.com/) it returns an object with 2 points that look like the following;
"Noshido__LK":"Ad8f......yd78y="
(128/256 key?)
When I ping the payroll api (https://business.na.deputy.com/api/v1/resource/TimesheetPayReturn) using the token it, Data Factory tells me that I 'need to use Login, OAuth2.0, or Bearer'.
I am not familiar with these type of connections, and am hoping to work within the parameters.
Thank You!
The short answer is yes. Permanent tokens is the easier way to access the data. The instructions on how to generate the permanent token is found in the GIF animation here:
https://www.deputy.com/api-doc/API/Authentication/
This is the code I use in Excel VBA to read the data
Sub Get_Data()
Dim request As New WinHttpRequest
Dim url As String, parameters As String
Dim permanentAccessToken As String
'Demo credentials
permanentAccessToken = "892******************b3e05f0df0"
url = "https://business.na.deputy.com/"
url = url & "api/v1/resource/TimesheetPayReturn"
request.Open "GET", url
request.SetRequestHeader "Authorization", "OAuth " + permanentAccessToken
request.Send
' Check the result is valid
If request.Status <> 200 Then
MsgBox "Error: " & request.ResponseText
Exit Sub
End If
End Sub
The permanent token is added to the header and that should work.

Using OAuth2 how do I pull the access token into a variable?

I am trying to make a call to an authorization endpoint using OAuth2 with grant type Client Credentials - my call is successful - that is not an issue. However, I, now, want to take the access token that is returned and put it in a variable so I may use it in subsequent calls without having to manually cut-and-paste to my other calls.
When the call returns I see the token I desire to copy in the Access Token field at the bottom of the OAuth2 window (the one shown below that says expires in 42 minutes) AND I see it in the Authorization field on the Timeline tab of the results. I just can't figure out how to get access to it so I may dump it into variable.
The gif on the FAQ goes really fast, and does not provide step by step. Also, I didnt find any answer on YouTube or other websites, so I thought to share step by step for chaining requests on Insomnia.
Create a POST query to obtain your access token. Notice that my access token is returned in the field called "access_token", we will use this in step 3. Your return field may be different.
Create a second GET request for the API that would return the data for you. In my case, I wanted to get all users from a SCIM interface. In the Bearer tab, type in Response => Body Attribute (Insomnia will autofill).
Mouse click on the Request => Body Attribute (the one you just typed in), and select the authentication post in the dropdown "Request" (this is the one you created in step 1), and in the "Filter (JSONPath)" field, type in the $.[attribute name] - where attribute name is the response that returns from authentication call. In my case, it was access_token, see step 1 for yours.
Enjoy!!
Click No Environment > Manage Environments and you will see a base environment in JSON.
Since this is in JSON, create a { "jwt_token": "Response => Body Attribute" }" pair for your token variable. Please note that "Response => Body Attribute" needs to be configured. When you type response, hit space and this option should be available.
Once done choosing "Response => Body Attribute", it will show with some gibberish content and with red background, no worries... just click it to configure. Make sure you have the same setup.
However... you need to change your request to the route where you get the token from the server and another thing is the Filter (JSONPath or XPath) change it depending on your setup.
You should have the token, stored in jwt_token variable and can use the variable on a route that you like.
Example:
If you want to save a token that is returned in a response into an environment variable, you can use request chaining in your environment variable. Take a look at this url for more details on that https://support.insomnia.rest/article/43-chaining-requests...
Here is what you could do (what I did)
Create an environment variable
For the value of the variable, use the Response => Body Attribute and under Filter (JSONPath or XPath), choose the attribute of the token in your response body (if it is "token" then put $.token).
After that just put the token environment variable wherever you need it in the following requests.
I was not able to resolve this question but was able to get around it by defining the fields in the body of the request and bypassing the OAuth2 tab completely.
You can add it as a header, by referencing the outputs of the OAuth2 request:

The "state" param from the URL and session do not match

In facebook documantion
require('include/facebook/autoload.php'); //SDK directory
$fb = new Facebook\Facebook([
'app_id' => '***********',
'app_secret' => '***********************'
]);
$helper = $fb->getRedirectLoginHelper();
$permissions = ['email', 'public_profile']; // optional
$loginUrl = $helper->getLoginUrl('http://www.meusite.com.br/login-callback.php', $permissions);
When direct it to the url $loginUrl, the return is:
Facebook SDK returned an error: Cross-site request forgery validation failed. The "state" param from the URL and session do not match
I had the same error.
The problem occurred because I did getLoginUrl(...) before getAccessToken()
So rid of getLoginUrl(...) in redirected URL and code should works.
I had the same issue and for me that error was occurring because I did not put session_start(); in my login.php page code before calling getLoginUrl(..) and also at the top of login-callback.php page.
Just put session_start(); in your "login" page and "login-callback" page and it will work surely just like it is working for me now.
There could be 2 reason for this error:
you didn't call session_start(); before getLoginUrl call
You executed getLoginUrl again in login-callback.php, so state value regenerated and mismatched with the redirected value
Possible Fixes : I used the following configuration settings .
Enable WebAuthLogin under the advanced tab . Provide the url in the WebAuthLogin settins as same as that you provide in $loginUrl ;
For example if you use $loginUrl as https://example.com/ use that same in the WebAuthlogin Url
$loginUrl = $helper->getLoginUrl('https://example.com/', $permissions);
This problem occures also in case that you generate 2 or more login links on the same page (e.g. one for login and other for registration - even both point to the same url, they have just different labels).
Facebook SDK creates/updates $_SESSION[FBRLH_state] for each new generated loginURL. So if there are 2 generated URLs (using $helper->getLoginUrl()) then the $_SESSION[FBRLH_state] is 2-times rewritten and valid only for the last generated URL. Previous login URL becomes invalid. It means that it is not possible to generate 2 valid loginURLs. In case that 2 same URLs are generated then return the first one and avoid call of Facebook SDK for generation of second one.
I had the same problem.
The reason for this error is because --->
When "$helper->getLoginUrl" calls, it create a session variable "FB_State", and this is something to FB uses to match the token. Every-time getLoginUrl calls, it create new state. Then after user authorized and redirect back, if you codes cannot detect this event and re-run "$helper->getLoginUrl", then this error will occur.
The solution ->
refine your coding, stop run "$helper->getLoginUrl" again if authorized.
if you already rerun, then set the session variable for the token to NULL if you have, then User can re-authorize again.
when user tries re-authorize, they can remove the authorized APP once or you need to generate new link with "$helper->getReRequestUrl"
Yet, token has be called by "getAccessToken()" before the "$helper->getLoginUrl" or "$helper->getReRequestUrl" runs.
Good Luck!!!!!
Finally, looking into FB code, I discovered that the problem "Cross-site request forgery validation failed. Required param “state” missing" and similars are caused by PHP variable $_SESSION['FBRLH_state'] that for some "strange" reason when FB call the login-callback file.
To solve it I store this variable "FBRLH_state" AFTER the call of function $helper->getLoginUrl(...). Is very important to do only after the call of this function due to is inside this function when the variable $_SESSION['FBRLH_state'] is populated.
Below an example of my code in the login.php:
$uri=$helper->getLoginUrl($uri, $permissions);
foreach ($_SESSION as $k=>$v) {
if(strpos($k, "FBRLH_")!==FALSE) {
if(!setcookie($k, $v)) {
//what??
} else {
$_COOKIE[$k]=$v;
}
}
}
var_dump($_COOKIE);
And in the login-callback.php before calling all FB code:
foreach ($_COOKIE as $k=>$v) {
if(strpos($k, "FBRLH_")!==FALSE) {
$_SESSION[$k]=$v;
}
}
Last, but not least, remember also to include code for PHP session so..
if(!session_id()) {
session_start();
}
...
...
...
...
<?php session_write_close() ?>
I hope this response can help you to save 8-10 hours of work :)
Bye, Alex.
This issue was a bit confusing for me, because I had to change a line at the facebook src file:
src/Facebook/Helpers/FacebookRedirectLoginHelper.php
at the function: "validateCsrf" like this:
if ($result !== 0) {
throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
}
And change it into:
if ($result === 0) {
throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
}
I don't know if this makes a violation to the facebook SDK security, so I truly opened to any exlanation or recommendation for this answer.
You may also make the following changes at the facebook app manager:
add your site and callback-url into your facebook app account at:
setting->advanced:Valid OAuth redirect URIs
Don't forget to add another url with slash (/) at the end of each url and check all 4 checkboxes at Client OAuth Settings.
I had the same error. Are you using 1 file or 2? I was trying to get by using 1 file but my error was resolved when I split into login.php & fb-callback.php as the documentation recommended. My sessions were being re-written so the state was never saved properly.
Good luck!
Happens when the session in missing a needed variable.
might be caused by several things.
In my case I left the "www" out of the callback URL
You could actually be parsing the data from another domain... for example:
website.com is different from www .website.com
If you're parsing data from http ://website.com/login.php to http://www.website.com/fb-callback.php this would be a cross-domain problem and the error you are receiving would be because of that....
http ://website.com and http ://www.website.com are the same but the script identifies them as different..... hope that gives insight to the problem.

Not receiving Google OAuth refresh token

I want to get the access token from Google. The Google API says that to get the access token, send the code and other parameters to token generating page, and the response will be a JSON Object like :
{
"access_token" : "ya29.AHES6ZTtm7SuokEB-RGtbBty9IIlNiP9-eNMMQKtXdMP3sfjL1Fc",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/HKSmLFXzqP0leUihZp2xUt3-5wkU7Gmu2Os_eBnzw74"
}
However, I'm not receiving the refresh token. The response in my case is:
{
"access_token" : "ya29.sddsdsdsdsds_h9v_nF0IR7XcwDK8XFB2EbvtxmgvB-4oZ8oU",
"token_type" : "Bearer",
"expires_in" : 3600
}
The refresh_token is only provided on the first authorization from the user. Subsequent authorizations, such as the kind you make while testing an OAuth2 integration, will not return the refresh_token again. :)
Go to the page showing Apps with access to your account:
https://myaccount.google.com/u/0/permissions.
Under the Third-party apps menu, choose your app.
Click Remove access and then click Ok to confirm
The next OAuth2 request you make will return a refresh_token (providing that it also includes the 'access_type=offline' query parameter.
Alternatively, you can add the query parameters prompt=consent&access_type=offline to the OAuth redirect (see Google's OAuth 2.0 for Web Server Applications page).
This will prompt the user to authorize the application again and will always return a refresh_token.
In order to get the refresh token you have to add both approval_prompt=force and access_type="offline"
If you are using the java client provided by Google it will look like this:
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, getClientSecrets(), scopes)
.build();
AuthorizationCodeRequestUrl authorizationUrl =
flow.newAuthorizationUrl().setRedirectUri(callBackUrl)
.setApprovalPrompt("force")
.setAccessType("offline");
I'd like to add a bit more info on this subject for those frustrated souls who encounter this issue. The key to getting a refresh token for an offline app is to make sure you are presenting the consent screen. The refresh_token is only returned immediately after a user grants authorization by clicking "Allow".
The issue came up for me (and I suspect many others) after I'd been doing some testing in a development environment and therefore already authorized my application on a given account. I then moved to production and attempted to authenticate again using an account which was already authorized. In this case, the consent screen will not come up again and the api will not return a new refresh token. To make this work, you must force the consent screen to appear again by either:
prompt=consent
or
approval_prompt=force
Either one will work but you should not use both. As of 2021, I'd recommend using prompt=consent since it replaces the older parameter approval_prompt and in some api versions, the latter was actually broken (https://github.com/googleapis/oauth2client/issues/453). Also, prompt is a space delimited list so you can set it as prompt=select_account%20consent if you want both.
Of course you also need:
access_type=offline
Additional reading:
Docs: https://developers.google.com/identity/protocols/oauth2/web-server#request-parameter-prompt
Docs: https://developers.google.com/identity/protocols/oauth2/openid-connect#re-consent
Discussion about this issue: https://github.com/googleapis/google-api-python-client/issues/213
I searched a long night and this is doing the trick:
Modified user-example.php from admin-sdk
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$authUrl = $client->createAuthUrl();
echo "<a class='login' href='" . $authUrl . "'>Connect Me!</a>";
then you get the code at the redirect url
and the authenticating with the code and getting the refresh token
$client()->authenticate($_GET['code']);
echo $client()->getRefreshToken();
You should store it now ;)
When your accesskey times out just do
$client->refreshToken($theRefreshTokenYouHadStored);
This has caused me some confusion so I thought I'd share what I've come to learn the hard way:
When you request access using the access_type=offline and approval_prompt=force parameters you should receive both an access token and a refresh token. The access token expires soon after you receive it and you will need to refresh it.
You correctly made the request to get a new access token and received the response that has your new access token. I was also confused by the fact that I didn't get a new refresh token. However, this is how it is meant to be since you can use the same refresh token over and over again.
I think some of the other answers assume that you wanted to get yourself a new refresh token for some reason and sugggested that you re-authorize the user but in actual fact, you don't need to since the refresh token you have will work until revoked by the user.
Rich Sutton's answer finally worked for me, after I realized that adding access_type=offline is done on the front end client's request for an authorization code, not the back end request that exchanges that code for an access_token. I've added a comment to his answer and this link at Google for more info about refreshing tokens.
P.S. If you are using Satellizer, here is how to add that option to the $authProvider.google in AngularJS.
In order to get the refresh_token you need to include access_type=offline in the OAuth request URL. When a user authenticates for the first time you will get back a non-nil refresh_token as well as an access_token that expires.
If you have a situation where a user might re-authenticate an account you already have an authentication token for (like #SsjCosty mentions above), you need to get back information from Google on which account the token is for. To do that, add profile to your scopes. Using the OAuth2 Ruby gem, your final request might look something like this:
client = OAuth2::Client.new(
ENV["GOOGLE_CLIENT_ID"],
ENV["GOOGLE_CLIENT_SECRET"],
authorize_url: "https://accounts.google.com/o/oauth2/auth",
token_url: "https://accounts.google.com/o/oauth2/token"
)
# Configure authorization url
client.authorize_url(
scope: "https://www.googleapis.com/auth/analytics.readonly profile",
redirect_uri: callback_url,
access_type: "offline",
prompt: "select_account"
)
Note the scope has two space-delimited entries, one for read-only access to Google Analytics, and the other is just profile, which is an OpenID Connect standard.
This will result in Google providing an additional attribute called id_token in the get_token response. To get information out of the id_token, check out this page in the Google docs. There are a handful of Google-provided libraries that will validate and “decode” this for you (I used the Ruby google-id-token gem). Once you get it parsed, the sub parameter is effectively the unique Google account ID.
Worth noting, if you change the scope, you'll get back a refresh token again for users that have already authenticated with the original scope. This is useful if, say, you have a bunch of users already and don't want to make them all un-auth the app in Google.
Oh, and one final note: you don't need prompt=select_account, but it's useful if you have a situation where your users might want to authenticate with more than one Google account (i.e., you're not using this for sign-in / authentication).
1. How to get 'refresh_token' ?
Solution: access_type='offline' option should be used when generating authURL.
source : Using OAuth 2.0 for Web Server Applications
2. But even with 'access_type=offline', I am not getting the 'refresh_token' ?
Solution: Please note that you will get it only on the first request, so if you are storing it somewhere and there is a provision to overwrite this in your code when getting new access_token after previous expires, then make sure not to overwrite this value.
From Google Auth Doc : (this value = access_type)
This value instructs the Google authorization server to return a
refresh token and an access token the first time that your application
exchanges an authorization code for tokens.
If you need 'refresh_token' again, then you need to remove access for your app as by following the steps written in Rich Sutton's answer.
I'm using nodejs client for access to private data
The solution was add the promp property with value consent to the settings object in oAuth2Client.generateAuthUrl function.
Here is my code:
const getNewToken = (oAuth2Client, callback) => {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
prompt: 'consent',
scope: SCOPES,
})
console.log('Authorize this app by visiting this url:', authUrl)
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
rl.question('Enter the code from that page here: ', (code) => {
rl.close()
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error while trying to retrieve access token', err)
oAuth2Client.setCredentials(token)
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err)
console.log('Token stored to', TOKEN_PATH)
})
callback(oAuth2Client)
})
})
}
You can use the online parameters extractor to get the code for generate your token:
Online parameters extractor
Here is the complete code from google official docs:
https://developers.google.com/sheets/api/quickstart/nodejs
I hope the information is useful
Setting this will cause the refresh token to be sent every time:
$client->setApprovalPrompt('force');
an example is given below (php):
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope("email");
$client->addScope("profile");
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
For me I was trying out CalendarSampleServlet provided by Google. After 1 hour the access_key times out and there is a redirect to a 401 page. I tried all the above options but they didn't work. Finally upon checking the source code for 'AbstractAuthorizationCodeServlet', I could see that redirection would be disabled if credentials are present, but ideally it should have checked for refresh token!=null. I added below code to CalendarSampleServlet and it worked after that. Great relief after so many hours of frustration . Thank God.
if (credential.getRefreshToken() == null) {
AuthorizationCodeRequestUrl authorizationUrl = authFlow.newAuthorizationUrl();
authorizationUrl.setRedirectUri(getRedirectUri(req));
onAuthorization(req, resp, authorizationUrl);
credential = null;
}
Using offline access and prompt:consent worked well to me:
auth2 = gapi.auth2.init({
client_id: '{cliend_id}'
});
auth2.grantOfflineAccess({prompt:'consent'}).then(signInCallback);
In order to get new refresh_token each time on authentication the type of OAuth 2.0 credentials created in the dashboard should be "Other". Also as mentioned above the access_type='offline' option should be used when generating the authURL.
When using credentials with type "Web application" no combination of prompt/approval_prompt variables will work - you will still get the refresh_token only on the first request.
To get a refresh token using postman, here is an example of the configurations
Expected Response
now google had refused those parameters in my request (access_type, prompt)... :( and there is no "Revoke Access" button at all. I'm frustrating because of getting back my refresh_token lol
UPDATE:
I found the answer in here :D you can get back the refresh token by a request
https://developers.google.com/identity/protocols/OAuth2WebServer
curl -H "Content-type:application/x-www-form-urlencoded" \
https://accounts.google.com/o/oauth2/revoke?token={token}
The token can be an access token or a refresh token. If the token is an access token and it has a corresponding refresh token, the refresh token will also be revoked.
If the revocation is successfully processed, then the status code of the response is 200. For error conditions, a status code 400 is returned along with an error code.
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010_000;
use utf8;
binmode STDOUT, ":encoding(utf8)";
use Text::CSV_XS;
use FindBin;
use lib $FindBin::Bin . '/../lib';
use Net::Google::Spreadsheets::V4;
use Net::Google::DataAPI::Auth::OAuth2;
use lib 'lib';
use Term::Prompt;
use Net::Google::DataAPI::Auth::OAuth2;
use Net::Google::Spreadsheets;
use Data::Printer ;
my $oauth2 = Net::Google::DataAPI::Auth::OAuth2->new(
client_id => $ENV{CLIENT_ID},
client_secret => $ENV{CLIENT_SECRET},
scope => ['https://www.googleapis.com/auth/spreadsheets'],
);
my $url = $oauth2->authorize_url();
# system("open '$url'");
print "go to the following url with your browser \n" ;
print "$url\n" ;
my $code = prompt('x', 'paste code: ', '', '');
my $objToken = $oauth2->get_access_token($code);
my $refresh_token = $objToken->refresh_token() ;
print "my refresh token is : \n" ;
# debug p($refresh_token ) ;
p ( $objToken ) ;
my $gs = Net::Google::Spreadsheets::V4->new(
client_id => $ENV{CLIENT_ID}
, client_secret => $ENV{CLIENT_SECRET}
, refresh_token => $refresh_token
, spreadsheet_id => '1hGNULaWpYwtnMDDPPkZT73zLGDUgv5blwJtK7hAiVIU'
);
my($content, $res);
my $title = 'My foobar sheet';
my $sheet = $gs->get_sheet(title => $title);
# create a sheet if does not exit
unless ($sheet) {
($content, $res) = $gs->request(
POST => ':batchUpdate',
{
requests => [
{
addSheet => {
properties => {
title => $title,
index => 0,
},
},
},
],
},
);
$sheet = $content->{replies}[0]{addSheet};
}
my $sheet_prop = $sheet->{properties};
# clear all cells
$gs->clear_sheet(sheet_id => $sheet_prop->{sheetId});
# import data
my #requests = ();
my $idx = 0;
my #rows = (
[qw(name age favorite)], # header
[qw(tarou 31 curry)],
[qw(jirou 18 gyoza)],
[qw(saburou 27 ramen)],
);
for my $row (#rows) {
push #requests, {
pasteData => {
coordinate => {
sheetId => $sheet_prop->{sheetId},
rowIndex => $idx++,
columnIndex => 0,
},
data => $gs->to_csv(#$row),
type => 'PASTE_NORMAL',
delimiter => ',',
},
};
}
# format a header row
push #requests, {
repeatCell => {
range => {
sheetId => $sheet_prop->{sheetId},
startRowIndex => 0,
endRowIndex => 1,
},
cell => {
userEnteredFormat => {
backgroundColor => {
red => 0.0,
green => 0.0,
blue => 0.0,
},
horizontalAlignment => 'CENTER',
textFormat => {
foregroundColor => {
red => 1.0,
green => 1.0,
blue => 1.0
},
bold => \1,
},
},
},
fields => 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment)',
},
};
($content, $res) = $gs->request(
POST => ':batchUpdate',
{
requests => \#requests,
},
);
exit;
#Google Sheets API, v4
# Scopes
# https://www.googleapis.com/auth/drive View and manage the files in your Google D# # i# rive
# https://www.googleapis.com/auth/drive.file View and manage Google Drive files and folders that you have opened or created with this app
# https://www.googleapis.com/auth/drive.readonly View the files in your Google Drive
# https://www.googleapis.com/auth/spreadsheets View and manage your spreadsheets in Google Drive
# https://www.googleapis.com/auth/spreadsheets.readonly View your Google Spreadsheets
My solution was a bit weird..i tried every solution i found on internet and nothing. Surprisely this worked: delete the credentials.json, refresh, vinculate your app in your account again. The new credentials.json file will have the refresh token. Backup this file somewhere.
Then keep using your app until the refresh token error comes again. Delete the crendetials.json file that now is only with an error message (this hapenned in my case), then paste you old credentials file in the folder, its done!
Its been 1 week since ive done this and had no more problems.
Adding access_type=offline to the authorisation Google authorisation URL did the trick for me. I am using Java and Spring framework.
This is the code that creates the client registration:
return CommonOAuth2Provider.GOOGLE
.getBuilder(client)
.scope("openid", "profile", "email", "https://www.googleapis.com/auth/gmail.send")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth?access_type=offline")
.clientId(clientId)
.redirectUriTemplate("{baseUrl}/{action}/oauth2/code/{registrationId}")
.clientSecret(clientSecret)
.build();
The important part here is the authorization URI, to which ?access_type=offline is appended.

Nothing except "None" returned for my Python web.py Facebook app when I turn on "OAuth 2.0 for Canvas"

I am a beginning Facebook app developer, but I'm an experienced developer. I'm using web.py as my web framework, and to make matters a bit worse, I'm new to Python.
I'm running into an issue, where when I try to switch over to using the newer "OAuth 2.0 for Canvas", I simply can't get anything to work. The only thing being returned in my Facebook app is "None".
My motivation for turning on OAuth 2.0 is because it sounds like Facebook is going to force it by July, and I might as well learn it now and now have to rewrite it in a few weeks.
I turned on "OAuth 2.0 for Canvas" in the Advanced Settings, and rewrote my code to look for "signed_request" that is POSTed to my server whenever my test user tries to access my app.
My code is the following (I've removed debugging statements and error checking for brevity):
#!/usr/bin/env python
import base64
import web
import minifb
import urllib
import json
FbApiKey = "AAAAAA"
FbActualSecret = "BBBBBB"
CanvasURL = "http://1.2.3.4/fb/"
RedirectURL="http://apps.facebook.com/CCCCCCCC/"
RegURL = 'https://graph.facebook.com/oauth/authorize?client_id=%s&redirect_uri=%s&type=user_agent&display=page' % (FbApiKey, RedirectURL)
urls = (
'/fb/', 'index',
)
app = web.application(urls, locals())
def authorize():
args = web.input()
signed_request = args['signed_request']
#split the signed_request via the .
strings = signed_request.split('.')
hmac = strings[0]
encoded = strings[1]
#since uslsafe_b64decode requires padding, add the proper padding
numPads = len(encoded) % 4
encoded = encoded + "=" * numPads
unencoded = base64.urlsafe_b64decode(str(encoded))
#convert signedRequest into a dictionary
signedRequest = json.loads(unencoded)
try:
#try to find the oauth_token, if it's not there, then
#redirect to the login page
access_token = signedRequest['oauth_token']
print(access_token)
except:
print("Access token not found, redirect user to login")
redirect = "<script type=\"text/javascript\">\ntop.location.href=\"" +_RegURL + "\";\n</script>"
print(redirect)
return redirect
# Do something on the canvas page
returnString = "<html><body>Hello</body></html>"
print(returnString)
class index:
def GET(self):
authorize()
def POST(self):
authorize()
if __name__ == "__main__":
app.run()
For the time being, I want to concentrate on the case where the user is already logged in, so assume that oauth_token is found.
My question is: Why is my "Hello" not being outputted, and instead all I see is "None"?
It appears that I'm missing something very fundamental, because I swear to you, I've scoured the Internet for solutions, and I've read the Facebook pages on this many times. Similarly, I've found many good blogs and stackoverflow questions that document precisely how to use OAuth 2.0 and signed_request. But the fact that I am getting a proper oauth_token, but my only output is "None" makes me think there is something fundamental that I'm doing incorrectly. I realize that "None" is a special word in python, so maybe that's the cause, but I can't pin down exactly what I'm doing wrong.
When I turn off OAuth 2.0, and revert my code to look for the older POST data, I'm able to easily print stuff to the screen.
Any help on this would be greatly appreciated!
How embarrassing!
In my authorize function, I return a string. But since class index is calling authorize, it needs to be returned from the class, not from authorize. If I return the return from authorize, it works.