Reputation: 428
I am trying to use Google Scripts to create an add-on to use in a Google Spreadsheet.
In the add-on I am connecting to third party APIs (Pocket APIs here) which require me to do OAuth. While using OAuth, I need to give a Redirect URI when authorisation API of Pocket is called.
As per information on this link: https://github.com/gsuitedevs/apps-script-oauth2, the redirect URI in such cases is of the format https://script.google.com/macros/d/{SCRIPT ID}/usercallback. I used this format, but I am getting a "Unable to open file" kind of error. I used "OAuth2.getRedirectUri()" to get the Redirect URI.
Clarification on the comment that I got: I am not opening Redirect URI directly. I have specified the it in my call to Pocket API. Here's the complete code:
/**
* @OnlyCurrentDoc Limits the script to only accessing the current spreadsheet.
*/
var API_KEY = '87572-369f6490c104433f539c40d6';
var FIRST_TOKEN = '';
/**
* Adds a custom menu with items to show the sidebar and dialog.
*
* @param {Object} e The event parameter for a simple onOpen trigger.
*/
function onOpen(e) {
SpreadsheetApp.getUi()
.createAddonMenu()
.addItem('Show articles', 'getPocketArticles')
.addToUi();
}
/**
* Runs when the add-on is installed; calls onOpen() to ensure menu creation and
* any other initializion work is done immediately.
*
* @param {Object} e The event parameter for a simple onInstall trigger.
*/
function onInstall(e) {
onOpen(e);
}
/*
*/
function getPocketArticles() {
// URL and params for the Pocket API
var root = 'https://getpocket.com/v3/';
var pocketService = getPocketService();
getPocketToken(root);
}
function getPocketToken(root) {
var url = root + 'oauth/request';
var payloadData = {
'consumer_key': API_KEY,
'redirect_uri': OAuth2.getRedirectUri(),
'state': ScriptApp.newStateToken()
.withMethod(usercallback)
.withTimeout(3600)
.createToken()
};
// parameters for url fetch
var params = {
'method': 'POST',
'contentType': 'application/json; charset=UTF8',
'headers': {
'X-Accept' : 'application/json'
},
'payload': JSON.stringify(payloadData)
};
Logger.log(params);
// call the Pocket API
var response = UrlFetchApp.fetch(url, params);
var data = response.getContentText();
var json = JSON.parse(data);
FIRST_TOKEN = json['code'];
Logger.log("First token: "+FIRST_TOKEN);
showSidebar(root);
}
/*
* Register the Pocket Service
*/
function getPocketService() {
// Create a new service with the given name. The name will be used when
// persisting the authorized token, so ensure it is unique within the
// scope of the property store.
return OAuth2.createService('pocket')
// Set the endpoint URLs.
//.setAuthorizationBaseUrl('https://getpocket.com/auth/authorize')
.setAuthorizationBaseUrl('https://getpocket.com/auth/authorize?request_token='+FIRST_TOKEN)
/*.setTokenUrl('')*/
// Set the client ID and secret, from the Google Developers Console.
.setClientId(API_KEY)
.setClientSecret(FIRST_TOKEN)
// Set the name of the callback function in the script referenced
// above that should be invoked to complete the OAuth flow.
.setCallbackFunction('usercallback')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
// Set the scopes to request (space-separated for Google services).
/*.setScope('')*/
// Below are Google-specific OAuth2 parameters.
// Sets the login hint, which will prevent the account chooser screen
// from being shown to users logged in with multiple accounts.
//.setParam('login_hint', Session.getActiveUser().getEmail())
// Requests offline access.
//.setParam('access_type', 'offline')
// Forces the approval prompt every time. This is useful for testing,
// but not desirable in a production application.
//.setParam('approval_prompt', 'force');
.setTokenHeaders({
/*'Authorization': 'Basic ' +
Utilities.base64Encode(CLIENT_ID + ':' + CLIENT_SECRET),*/
'request_token': FIRST_TOKEN,
'X-Accept' : 'application/json'
})
/*
// Avoid "invalid_client error".
// This service does not support form field authentication.
.setTokenPayloadHandler(function(tokenPayload) {
delete tokenPayload.client_id;
return tokenPayload;
})*/
// Setting Payload
.setTokenPayloadHandler(function(tokenPayload) {
tokenPayload.state = ScriptApp.newStateToken()
.withMethod(usercallback)
.withTimeout(3600)
.createToken();
return tokenPayload;
})
;
}
/**
* Opens a sidebar. The sidebar structure is described in the Sidebar.html
* project file.
*/
function showSidebar(root) {
var pocketService = getPocketService();
var authorizationUrl = pocketService.getAuthorizationUrl();
Logger.log("Authorization URL: " + authorizationUrl);
if (!pocketService.hasAccess()) {
var template = HtmlService.createTemplate(
'<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. ' +
'Reopen the sidebar when the authorization is complete.');
template.authorizationUrl = authorizationUrl;
var page = template.evaluate();
SpreadsheetApp.getUi().showSidebar(page);
} else {
// ...
getPermanentToken(root);
}
}
/**
*
*/
function getPermanentToken(root) {
var url = root + 'oauth/authorize';
var payloadData = {
'consumer_key': API_KEY,
'code': FIRST_TOKEN
};
// parameters for url fetch
var params = {
'method': 'POST',
'muteHttpExceptions': false,
'contentType': 'application/json; charset=UTF8',
'headers': {
'X-Accept' : 'application/json'
},
'payload': JSON.stringify(payloadData)
};
/*'contentType': 'application/json',*/
// call the Pocket API
Logger.log("URL: "+url+"\nparams: "+JSON.stringify(params));
var response = UrlFetchApp.fetch(url, params);
Logger.log("Response: "+response.getAllHeaders());
}
/**
* Handle Callback
*
*/
function usercallback(request) {
var pocketService = getPocketService();
var isAuthorized = pocketService.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
I have done this on the basis of the guidelines given on github link shared above, and the examples mentioned on https://github.com/gsuitedevs/apps-script-oauth2/tree/master/samples. These examples are not exactly applicable to me as Pocket's APIs seem to behave bit differently (or so I understand)
I have put logger statement for Authorization URL too. It is shown in the logs I shared below.
Execution Transcript logs:
[19-09-16 07:36:45:329 IST] Starting execution
[19-09-16 07:36:45:342 IST] PropertiesService.getUserProperties() [0 seconds]
[19-09-16 07:36:45:342 IST] ScriptApp.getScriptId() [0 seconds]
[19-09-16 07:36:45:344 IST] Logger.log([{headers={X-Accept=application/json}, method=POST, payload={"consumer_key":"87572-369f6490c104433f539c40d6","redirect_uri":"https://script.google.com/macros/d/1QWlUgkmucy_wXK3v1BUjbxh9Ei1bBan7AFL2Jce...) [0 seconds]
[19-09-16 07:36:45:513 IST] UrlFetchApp.fetch([https://getpocket.com/v3/oauth/request, {headers={X-Accept=application/json}, method=POST, payload={"consumer_key":"87572-369f6490c104433f539c40d6","redirect_uri":"https://script.google.com/macros/d/...) [0.168 seconds]
[19-09-16 07:36:45:513 IST] HTTPResponse.getContentText() [0 seconds]
[19-09-16 07:36:45:514 IST] Logger.log([First token: 5c9102f2-3feb-caa6-86f0-c4530e, []]) [0 seconds]
[19-09-16 07:36:45:515 IST] PropertiesService.getUserProperties() [0 seconds]
[19-09-16 07:36:45:516 IST] ScriptApp.newStateToken() [0 seconds]
[19-09-16 07:36:45:516 IST] StateTokenBuilder.withMethod([usercallback]) [0 seconds]
[19-09-16 07:36:45:517 IST] StateTokenBuilder.withArgument([serviceName, pocket]) [0 seconds]
[19-09-16 07:36:45:517 IST] StateTokenBuilder.withTimeout([3600]) [0 seconds]
[19-09-16 07:36:45:517 IST] ScriptApp.getScriptId() [0 seconds]
[19-09-16 07:36:45:517 IST] StateTokenBuilder.createToken() [0 seconds]
[19-09-16 07:36:45:518 IST] Logger.log([Authorization URL: https://getpocket.com/auth/authorize?request_token=5c9102f2-3feb-caa6-86f0-c4530e&client_id=87572-369f6490c104433f539c40d6&response_type=code&redirect_uri=https%3A%2F%2Fscript.goog...) [0 seconds]
[19-09-16 07:36:45:523 IST] Properties.getProperty([oauth2.pocket]) [0.005 seconds]
[19-09-16 07:36:45:525 IST] HtmlService.createTemplate([<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. Reopen the sidebar when the authorization is complete.]) [0.001 seconds]
[19-09-16 07:36:45:525 IST] Function.apply([[]]) [0 seconds]
[19-09-16 07:36:45:526 IST] HtmlService.createHtmlOutput() [0 seconds]
[19-09-16 07:36:45:527 IST] HtmlOutput.append([<a href="]) [0 seconds]
[19-09-16 07:36:45:528 IST] HtmlOutput.appendUntrusted([https://getpocket.com/auth/authorize?request_token=5c9102f2-3feb-caa6-86f0-c4530e&client_id=87572-369f6490c104433f539c40d6&response_type=code&redirect_uri=https%3A%2F%2Fscript.google.com%2Fmacros%2Fd...) [0 seconds]
[19-09-16 07:36:45:528 IST] HtmlOutput.append([" target="_blank">Authorize</a>. Reopen the sidebar when the authorization is complete.]) [0 seconds]
[19-09-16 07:36:45:529 IST] HtmlOutput.append([]) [0 seconds]
[19-09-16 07:36:45:530 IST] SpreadsheetApp.getUi() [0 seconds]
[19-09-16 07:36:45:595 IST] Ui.showSidebar([HtmlOutput]) [0.064 seconds]
[19-09-16 07:36:45:658 IST] Execution succeeded [0.255 seconds total runtime]
Output of the logger statements that I have in my code:
[19-09-16 07:36:45:343 IST] {headers={X-Accept=application/json}, method=POST, payload={"consumer_key":"87572-369f6490c104433f539c40d6","redirect_uri":"https://script.google.com/macros/d/1QWlUgkmucy_wXK3v1BUjbxh9Ei1bBan7AFL2JceT2401-ztkEtFk9-xb/usercallback"}, contentType=application/json; charset=UTF8}
[19-09-16 07:36:45:514 IST] First token: 5c9102f2-3feb-caa6-86f0-c4530e
[19-09-16 07:36:45:518 IST] Authorization URL: https://getpocket.com/auth/authorize?request_token=5c9102f2-3feb-caa6-86f0-c4530e&client_id=87572-369f6490c104433f539c40d6&response_type=code&redirect_uri=https%3A%2F%2Fscript.google.com%2Fmacros%2Fd%2F1QWlUgkmucy_wXK3v1BUjbxh9Ei1bBan7AFL2JceT2401-ztkEtFk9-xb%2Fusercallback&state=ADEpC8z2oP8q4juC4cgceCqVZw34DEX3KTdN9Cm5fPX-Nh7vzYIDdw50GIHNMfQ--Y92uqA_K8RUaAaHf7OU9O72RPOQ3ryYBVTrlQ-ZLZRQRgJ5Re68KOTud8ckAnonjG24a5-W2ti7g3o5rQebaDnhIlTLjY2MJWrP68pf70FSak6nhby7B_quV6PCmIjbCfS0R54D6oV3tTCwrL9JpO62zxmIHLkceD0O-cZc8SUrJK1yMDBcofuZCqGIxjlBOVpnCvugSnhCczp3qCaEA-3cLj3jwzXDX4XluqX7c-0hWsrkgHQNFxiB7qFo7pzhd8NlBcVj4t6yoQyTfquYN84C1wGKxcjtfg
What happens is that when I invoke this URL I get redirected to Pocket authorisation page that asks me to authorise this add-on to have permission to access my Pocket data. That is fine, but when I click on Authorize, I get "Unable to open file" kind of error. This is the screenshot of how it appears: https://photos.app.goo.gl/VVCcEfh6NYTWK6MW9
I wonder where I am going wrong. Could someone please guide me?
I am new to Google Scripts and OAuth, so please pardon if this is a silly question.
Upvotes: 5
Views: 2310