Unexpected exception upon serializing continuation - google-docs-api

I get this error: Unexpected exception upon serializing continuation (not much help)
It is caused by the FetchUrlApp.fetch(); call. I akm using Google Apps Script for Sites, not Google Spreadsheets. The code works in the original instance but as soon as I copy and paste the code into a new project I get the above error message. I am accessing Google Docs APIs. I have read on other forums that I need authorization but I have been unable to gain the right authorization for the code to work. No prompt ever pops up when I run a copy of the code for the first time.
Code exert:
var oauthConfig = UrlFetchApp.addOAuthService("docs");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope=https://docs.google.com/feeds/");
oauthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oauthConfig.setConsumerKey(_consumerKey_);
oauthConfig.setConsumerSecret(_consumerSecret_);
var requestData3 = {
"method": "GET",
"headers": {"GData-Version": "3.0"},
"oAuthServiceName": "docs",
"oAuthUseToken": "always",
};
var url = "https://docs.google.com/feeds/" + userName + "/private/full/-/mine";
var result = UrlFetchApp.fetch(url, requestData3); //error occurs, any thoughts?
Thank you in advance,
James Krimm

You have to set both consumerKey and consumerSecret to "anonymous" in order to trigger the 3-legged OAuth process:
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
Replace your two lines with these and the authorization popup dialog will show up, allowing the user to grant access to its documents.

What I would suggest is to write a special function that does nothing else than call the Oauth process and call it from the script editor once.
As an example, here is the one I have used recently to make the authorize popup appear :
function authorize(){
// function to call from the script editor to authorize googleOauth
var id=mailtemplatedoc
var url = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(url+'download/documents/Export? exportFormat=html&format=html&id='+id,
googleOAuth_('docs',url)).getContentText();
}
EDIT : And the missing part I had forgotten :
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:name, oAuthUseToken:"always"};
}

Related

Correct way to send auth headers via ajax request to an API

I have a form where I've stored the auth key in a hidden field.
hidden_field_tag 'auth_key', Settings.biometric.auth_key
I am sending an ajax request to an API where I'm setting the auth key in the header which requires the key:
var authKey = $("input[id='auth_key']").val();
beforeSend: function (xhr) {
xhr.setRequestHeader ("auth", authKey);
}
All is working fine, just that the auth key resides in the form and is easily inspectable by any malicious user.
I think this might not be the right way to do this
What is the best approach to perform this?
TL;DR:
Anyone who has access to the client-side (a.k.a your User using a browser), can ultimately find a way to get this auth_key, because a client-user has and will always have power/access on what "data" gets sent/received/stored, especially easier here in webapps because of built-in browser developer tools.
Some Explanations:
Disclaimer: I am not well versed in this field, so if anyone, please let me know.
Yes, it can be encrypted in the client-side, but a user/hacker can decrypt them because the "encryption" trace can be found somewhere in your JS script file:
// application.js example
...beforeSend: function (xhr) {
var encryptedAuthKey = localstorage.get('encrypted-auth-key');
var decryptionPassword = 'abcd';
var authKey = doSomeFancyDecryption(encryptedAuthKey, decryptionPassword);
xhr.setRequestHeader ("auth", authKey);
}
However, a secure way would be to encrypt them using a password stored in the server-side, so that the client cannot debug/inspect/find this password in the JS code... except that this is not possible, see below:
// application.js example
...beforeSend: function (xhr) {
var encryptedAuthKey = localstorage.get('encrypted-auth-key');
var decryptionPassword = someFunctionThatPerformsAjaxRequestToServerAndReturnsTheDecryptionPassword();
var authKey = doSomeFancyDecryption(encryptedAuthKey, decryptionPassword);
function someFunctionThatPerformsAjaxRequestToServerAndReturnsTheDecryptionPassword() {
// do some ajax request with Auth header equals Something...
// ummm... what's the value of this something?
// ummm... I cannot pass in my Username and Password, of course!
// ummm... I cannot pass in another-kind of "auth_key", which just basically loops this process itself.
}
xhr.setRequestHeader ("auth", authKey);
}
You can "sign" your request so that you won't need to directly supply anymore the auth_key as part of your request (but the client-user can still hack this and get the auth-key and create their own request themselves, precisely because they can see and have access to your underlying code like below):
// application.js example
...beforeSend: function (xhr) {
var authKey = localstorage.get('auth-key');
var params = // assign all input fields as key-values object here
var url = this.url;
var signature = generateSignatureUsingHMAC(authKey, url, params)
xhr.setRequestHeader ("Signature", signature);
}
I would personally do something like the signature-based authorization above

Automated OAuth2 token not working - Google Apps Script

I am trying to run a google apps script, in a document, that sends an email with an attached google spreadsheet as an .xlsx file automatically, running every few hours.
Below is the solution that works if I use a manual OAuth2 code coming from the google OAuth2 playground :
function downloadXLS() {
var AUTH_TOKEN = "xxxx";
var auth = "AuthSub token=\"" + AUTH_TOKEN + "\"";
var file = Drive.Files.get('xxxx');
var response = UrlFetchApp.fetch('https://docs.google.com/spreadsheets/d/xxx/export?format=xlsx',{headers: {Authorization: auth}});
var doc = response.getBlob();
app = DriveApp.createFile(doc).setName(file.title + '.xls')
MailApp.sendEmail("xxx#xxx.com", "oh man", " Body", { attachments: app })
}
To try to auto-generate the authorization token I followed exactly all the steps here:
https://github.com/googlesamples/apps-script-oauth2
Changing on the script :
.setClientId('...')
.setClientSecret('...')
I also put in the URI the the Project Id inside the https://script.google.com/macros/d/myprojectkey/usercallback of the google developer console
But when i run the function makeRequest() it tells me : "Access not granted or expired"
So i wonder which step i missed.
Do you have any clue on what is going on ?
Help is much appreciated,
Thanks
You need to do step 2: Direct the user to the authorization URL
When the sidebar loads you will click the link and the Oauth dialog will open. After you allow access you can use the getAccessToken() method.
EDIT:
For your specific case you do not need a separate OAuth flow. You can use Apps Script to get the token you need to do that export. As you are already requesting access to drive your token will work for the export call.
function downloadXLS() {
var file = Drive.Files.get('xxxx');
var response = UrlFetchApp.fetch('https://docs.google.com/spreadsheets/d/xxx/export?format=xlsx',{headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
var doc = response.getBlob();
app = DriveApp.createFile(doc).setName(file.title + '.xls')
MailApp.sendEmail("xxx#xxx.com", "oh man", " Body", { attachments: app })
}

Creating Trello cards with Google Apps and OAuth

I'm trying to build a Google Apps Script that integrates with Trello, the idea being to use it to push information from spreadsheets and forms into the Trello API and create cards on a pending list on a certain board.
I found another question that pointed me in the right direction, and added in OAuth based on the GAS OAuth Documentation. The problem is I can't post the the board. I run the script, the OAuth prompt fires, and the script completes with no errors. I can also GET data from the private board, so I assume the authorization is working properly.
So, what am I doing wrong that prevents my script from POSTing to Trello?
Here's the code I'm working with:
var trelloKey = [Trello API key];
var trelloSecret = [Trello API key secret];
var trelloList = [the id of the list we're posting to];
var oauthConfig = UrlFetchApp.addOAuthService('trello');
oauthConfig.setAccessTokenUrl('https://trello.com/1/OAuthGetAccessToken');
oauthConfig.setRequestTokenUrl('https://trello.com/1/OAuthGetRequestToken');
oauthConfig.setAuthorizationUrl('https://trello.com/1/OAuthAuthorizeToken');
oauthConfig.setConsumerKey(trelloKey);
oauthConfig.setConsumerSecret(trelloSecret);
function createTrelloCard() {
//POST [/1/cards], Required permissions: write
var payload = {'name': 'apiUploadedCard',
'desc': 'description',
'pos': 'top',
'due': '',
'idList': trelloList};
var url = 'https://api.trello.com/1/cards'
var options = {'method' : 'post',
'payload' : payload,
'oAuthServiceName' : 'trello',
'oAuthUseToken' : 'always'};
UrlFetchApp.fetch(url, options);
}
You just need set fetch options contentType to application/json. I just resolved the same problem by this.
Try adding the scope=read,write in your authorization url.
from:
oauthConfig.setAuthorizationUrl('https://trello.com/1/OAuthAuthorizeToken');
to:
oauthConfig.setAuthorizationUrl("https://trello.com/1/OAuthAuthorizeToken?scope=read,write");

Google Script API + Oauth + Tumblr

Hello,
I'm trying to acess, perform a post, into Tumblr with Oauth api provided by Tumblr) http://tumblr.com/api). I'm using Google Script and I've tryied too many solutions but anyone worked. To implement i've basaed myself into this(https://developers.google.com/apps-script/articles/twitter_tutorial) Google script twitter tutorial, once on Tumblr API web page they say that twitter api is almost the same that tumblr.
Contextualizing,
I've already set the Oauth class methods with data below and substituted consumer and secret keys with values got from the api i've created.
var oauthConfig = UrlFetchApp.addOAuthService("tumblr");
oauthConfig.setAccessTokenUrl(
"http://www.tumblr.com/oauth/access_token");
oauthConfig.setRequestTokenUrl(
"http://www.tumblr.com/oauth/request_token");
oauthConfig.setAuthorizationUrl(
"http://www.tumblr.com/oauth/authorize");
oauthConfig.setConsumerKey(<i>consumerkey</i>);
oauthConfig.setConsumerSecret(<i>consumerSecret</i>);
Error,
The code below isnt working as it should be.
var requestData = {
"method": "POST",
"oAuthServiceName": "tumbler",
"oAuthUseToken": "always"
};
var result = UrlFetchApp.fetch(
"https://api.tumblr.com/v2/blog/{blog}.tumblr.com/post?type=text&body=word",
requestData);
The Script to Twitter is almost the same and it works. Im able to perform tweets.
var result = UrlFetchApp.fetch(
"https://api.twitter.com/1/statuses/update.json?status=" + tweet,
requestData);
Response From Server
Request failed for returned code 400. Server response: {"meta":{"status":400,"msg":"Bad Request"},"response":{"errors":["Post cannot be empty."]}}
Possible Solutions
A possible solution can work using this information(got from tumblr.com/api):
OAuth
The API supports the OAuth 1.0a Protocol, accepting parameters via the Authorization header, with the HMAC-SHA1 signature method only. There's probably already an OAuth client library for your platform.
My question is, what am I doing wrong?(my post inst empty, i have 2 params). Had anyone had the same problem? Someone has suggestions?
Thank You.
I don't know anything about the tumblr api, but your http post is empty (the oAuth parameters aren't in the post body, they're advanced options), the body of the post needs to go in the "payload" parameter. See the section "Advanced parameters" in the docs. Or, as you aren't using the post can't you use a get request instead? Remove the method: POST parameter (GET is the default).
Thank You very much Daniel. It worked now!!
Everybody that want use Tumblr + Google Script API + oAuth can use de code below to perform posts.
I created I Google Spreadsheet and then a script there. Before to be able to post I neded to create and app into tumblr.com/api and get secret and consumer keys. Also I've deployed the Google script as an web app(ensure that the version is the last one(the final code)) before to create a new version. After that you go tu publish > deploy as web app !
That twitter tutorial I put on my first question is the only path you need to conclude your job.
function authorize() {
var oauthConfig = UrlFetchApp.addOAuthService("tumblr");
oauthConfig.setAccessTokenUrl(
"http://www.tumblr.com/oauth/access_token");
oauthConfig.setRequestTokenUrl(
"http://www.tumblr.com/oauth/request_token");
oauthConfig.setAuthorizationUrl(
"http://www.tumblr.com/oauth/authorize");
oauthConfig.setConsumerKey(getConsumerKey());
oauthConfig.setConsumerSecret(getConsumerSecret());
var requestData = {
"oAuthServiceName": "tumblr",
"oAuthUseToken": "always"
};
var result = UrlFetchApp.fetch(
"http://api.tumblr.com/v2/blog/{your_blog}.tumblr.com/posts/queue",
requestData);
}
function doGet(e) {
var tweet = e.parameter.tumblr;
var app = UiApp.createApplication().setTitle("Approved");
var panel = app.createFlowPanel();
authorize();
var encodedTweet = encodeURIComponent(tweet);
var payload =
{
"body" : encodedTweet,
"type" : "text"
};
var requestData = {
"method" : "POST",
"oAuthServiceName": "tumblr",
"oAuthUseToken": "always",
"payload" : payload
};
try {
var result = UrlFetchApp.fetch(
"https://api.tumblr.com/v2/blog/{your_blog}.tumblr.com/post",
requestData);
panel.add(app.createLabel().setText("You have approved: \"" + tweet + "\""));
} catch (e) {
Logger.log(e);
panel.add(app.createLabel().setText(e));
}
app.add(panel);
return app;
}

Yelp API Google App Script OAuth

I am trying to use Google Apps Script to query the Yelp Search Api and put the results into a spreadsheet. I having issues making the call to yelp using this example as a model:
var consumerKey = "... register your app with Twitter ...";
var consumerSecret = "... register your app with Twitter ...");
var oauthConfig = UrlFetchApp.addOAuthService("twitter");
oauthConfig.setAccessTokenUrl("http://api.twitter.com/oauth/access_token");
oauthConfig.setRequestTokenUrl("http://api.twitter.com/oauth/request_token");
oauthConfig.setAuthorizationUrl("http://api.twitter.com/oauth/authorize");
oauthConfig.setConsumerKey(consumerKey);
oauthConfig.setConsumerSecret(consumerSecret);
// "twitter" value must match the argument to "addOAuthService" above.
var options = {
"oAuthServiceName" : "twitter",
"oAuthUseToken" : "always"
};
var url = "http://api.twitter.com/1/statuses/user_timeline.json";
var response = UrlFetchApp.fetch(url, options);
var tweets = JSON.parse(response.getContentText());
// Handle tweets
https://developers.google.com/apps-script/class_oauthconfig
This class only has methods for setting the access token URLs which Yelp doesn't appear to provide. They just provide the Token and Token Secret directly. I assumed that these would be set like the Consumer Key and Secret but I haven't found a way.
The Yelp API uses oAuth1.0a to authorize and identifiy the API caller not the end user that might be using the application. This is not like a Twitter scenario where you have to let your users login. Therefore, you dont need any access token URLs or other details. You are able to create all the necessary tokens to get started. Here is how your API console should look like once everything is setup (I've obfuscated my keys for obvious reasons) -
Now, you'll need to make the API calls from the server side using UrlFetchApp and not use the jQuery AJAX APIs as that Yelp API doesn't seem to allow CORS and JSONP is not allowed with HtmlService. Otherwise you'll get errors like this below in the console -
Lastly, here is some sample code to get you started. I based these off their JavaScript sample -
var auth = {
consumerKey: "YOURKEY",
consumerSecret: "YOURSECRET",
accessToken: "YOURTOKEN",
accessTokenSecret: "YOURTOKENSECRET",
};
var terms = 'food';
var near = 'San+Francisco';
var accessor = {
consumerSecret: auth.consumerSecret,
tokenSecret: auth.accessTokenSecret
};
var parameters = [];
parameters.push(['term', terms]);
parameters.push(['location', near]);
parameters.push(['oauth_consumer_key', auth.consumerKey]);
parameters.push(['oauth_consumer_secret', auth.consumerSecret]);
parameters.push(['oauth_token', auth.accessToken]);
var message = {
'action': 'http://api.yelp.com/v2/search',
'method': 'GET',
'parameters': parameters
};
OAuth.setTimestampAndNonce(message);
OAuth.SignatureMethod.sign(message, accessor);
var parameterMap = OAuth.getParameterMap(message.parameters);
parameterMap.oauth_signature = OAuth.percentEncode(parameterMap.oauth_signature)
var url = OAuth.addToURL(message.action,parameterMap);
var response = UrlFetchApp.fetch(url).getContentText();
var responseObject = Utilities.jsonParse(response);
//have my JSON object, do whatever we want here, like add to spreadsheets
I also added a couple of GS script files with the contents of the oAuth JS code and SHA1 JS code from the links provided (just copy paste into new files in the script editor). However, if you feel adventurous, you could also use the Utilities APIs to manually sign and encode the necessary oAuth params.
Hope this helps. I was able to get Yelp responses with all the provided samples.

Resources