I am trying to use "OAuth2 library for Google Apps" in my "Apps Script".
It works (I see dialog box, etc.) but in the end I get error message "callback function authCallback not found". How to fix this problem?
var clientId = "clientId";
var clientSecret = "clientSecret";
function authCallback(request) {
var isAuthorized = service.handleCallback(request);
if( isAuthorized ){
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
var service = OAuth2.createService("My");
service.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth');
service.setTokenUrl('https://accounts.google.com/o/oauth2/token');
service.setScope('https://www.googleapis.com/auth/androidpublisher');
service.setClientId(clientId);
service.setClientSecret(clientSecret);
service.setPropertyStore(PropertiesService.getUserProperties());
service.setCallbackFunction('authCallback');
service.setParam('access_type', 'offline');
service.setParam('login_hint', Session.getActiveUser().getEmail());
if(!service.hasAccess()) {
showSidebar();
}else{
console.log( "AccessToken: "+service.getAccessToken() );
}
function showSidebar(){
var template = HtmlService.createTemplate(
'Authorize. ' +
'Reopen the sidebar when the authorization is complete.');
var authorizationUrl = service.getAuthorizationUrl();
template.authorizationUrl = authorizationUrl;
var page = template.evaluate();
SpreadsheetApp.getUi().showSidebar(page);
}
I define functions as nested into script function myFunction().
This is works for function showSidebar() but function authCallback(request) must be in the root as external.
Related
If it is not possible , can i make any trick to open a msgbox then end the script if anyone try to access the script
Here is my code :
function onOpen(){
var ui = SpreadsheetApp.getUi();
var menu = ui.createMenu('Private');
menu.addItem('Clear Cols','clearAli');
menu.addItem('Enter Number of days','noOfDays');
menu.addItem('subject','myEmail')
menu.addToUi();
}
function clearAli(){
var ss = SpreadsheetApp.getActive();
ss.getRangeList(['G2:K108', 'M2:M108', 'O2:O108']).activate().clearContent();
}
function noOfDays(){
var
days = SpreadsheetApp.getUi().prompt('Please enter the number of days.').getResponseText();
SpreadsheetApp.getActiveSpreadsheet().getRange('H2:H5').setValue(days);
}
function myEmail(){
const
subject = SpreadsheetApp.getUi().prompt('Enter your subject line').getResponseText();GmailApp.sendEmail('noelaramouny#gmail.com,alinemanoukian04#gmail.com',subject,'https://docs.google.com/spreadsheets/d/1Z92UC7RAqScObY2vpAKlzIvoY4yihxRtqw_2LEB2-8g/edit?usp=sharing');
}
const whitelist = ['mail1', 'mail2'];
const user = Session.getActiveUser().getEmail();
if (!whitelist.includes(user)) {
Browser.msgBox('Not in whitelist');
return;
}
NEW INFORMATION
Nov 17, 2020 - I can run onOpen from the script without getting the permission error. I can run the function that displays data from the script or the menu (after I have run onOpen from the script). I don't think there were any changes to the code but here it is again. The manifest is definitely unchanged.
//#NotOnlyCurrentDoc
function globalVariables(){
var variables = {
// this is the Connect Four Game
sheetId: '1fmZCittj4ksstmhh8_t0O0csj8IDdwi9ohDDL5ZE7VA',
sheetUrl: 'https://docs.google.com/spreadsheets/d/1fmZCittj4ksstmhh8_t0O0csj8IDdwi9ohDDL5ZE7VA/edit?usp=sharing'
};
return variables; //return a dictionary of keys and values
}
function onOpen() {
Logger.log("In onOpen" );
var variables = globalVariables(); //load the Global variables
Logger.log("In onOpen sheetUrl from globalVariables: " + variables.sheetUrl );
try {
// var ss = SpreadsheetApp.openByUrl(variables.sheetUrl);
var ss = SpreadsheetApp.openById(variables.sheetId)
if (!ss) {
SlidesApp.getUi().alert("Spreadsheet not found!");
return;
}
} catch(e) {
SlidesApp.getUi().alert(e);
return;
}
SlidesApp.getUi()
.createMenu( 'Ask ?')
.addItem('BE1','BE1')
.addItem('BE2','BE2')
.addItem('BE3','BE3')
.addItem('BE4','BE4')
.addToUi();
prepareQuestions(ss);
Logger.log("After prepareQuestions");
setupSheets(ss); // only want to do this once
Logger.log("After setupSheets");
}
function setupSheets(ss) {
SlidesApp.getUi().alert("setupSheets() triggered");
var nextI = '1';
var cache = CacheService.getUserCache();
cache.put('nextI', nextI);
console.log("1. cache.put('nextI') = " + nextI);
SlidesApp.getUi().alert("End onOpne) = ");
}
function BE1() {
SlidesApp.getUi().alert("BE1() triggered");
var variables = globalVariables(); //load the Global variables
var sheetName = 'BE1';
var sheet = SpreadsheetApp.openById(variables.sheetId).getSheetByName('BE1');
if (!sheet) {
Logger.log("Sheet BE1 not found");
SlidesApp.getUi().alert("Sheet BE1 not found");
return;
}
getNextQ(sheet);
SlidesApp.getUi().alert("end BE1 function");
}
I am the creator of both the spreadsheet and the slides. They are in the same folder on My Drive.
I have added Oauth to the manifest in the Slide:
{
"timeZone": "America/Mexico_City",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets.readonly"
]
}
I have tried to open the Spreadsheet by ID and URL. The error is:
Exception: You do not have permission to call SpreadsheetApp.openById. Required permissions: https://www.googleapis.com/auth/spreadsheets
I have tried creating a trigger (I have no idea what this is for) but when creating it the only options were time based which does not seem useful. The presence of the trigger does not change the error.
The code bound to the Slide
//#NotOnlyCurrentDoc
function globalVariables(){
var variables = {
// this is the Connect Four Game
sheetId: '1fmZCittj4ksstmhh8_t0O0csj8IDdwi9ohDDL5ZE7VA',
sheetUrl: 'https://docs.google.com/spreadsheets/d/1fmZCittj4ksstmhh8_t0O0csj8IDdwi9ohDDL5ZE7VA/edit?usp=sharing'
};
return variables; //return a dictionary of keys and values
}
//function installableOpen() { // never starts
function onOpen() {
var variables = globalVariables(); //load the Global variables
try {
// var ss = SpreadsheetApp.openByUrl(variables.sheetUrl);
var ss = SpreadsheetApp.openById(variables.sheetId)
if (!ss) {
SlidesApp.getUi().alert("Spreadsheet not found!");
return;
}
} catch(e) {
SlidesApp.getUi().alert(e);
return;
}
etc...
I added "//#NotOnlyCurrentDoc" as this was recommended in some places.
Share status of spreadsheet
The purpose of accessing the spreadsheet is to get data to display in html with showModalDialog. I have wandered through information on custom menus, dialogs, and sidebars to Google Docs, Sheets, and Forms, custom functions and macros, web apps, add-ons but I don't understand any of these. I was hoping to keep this simple. Custom function says it cannot display html (https://developers.google.com/apps-script/guides/sheets/functions) so that would be useless.
Any ideas appreciated.
As result of comment I changed the manifest as follows but the error is unchanged.
{
"timeZone": "America/Mexico_City",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets"
]
}
= = = = = = = = = = = = = = = = = = = =
I received this sort of email after deleting the triggers
As the error message says:
For the method
SpreadsheetApp.openById()
you are required to provide the scope
https://www.googleapis.com/auth/spreadsheets
instead of
https://www.googleapis.com/auth/spreadsheets.readonly
If you do not want to provide this scope, consider binding your script to the spreadsheet and use the method
SpreadsheetApp.getActive()
which accepts the scope
https://www.googleapis.com/auth/spreadsheets.currentonly
CRAZY FIX - I connected to the slide to which the script was bound before connecting to the spreadsheet! Pure chance discovery but the permission error is gone.
function onOpen() {
var deck = SlidesApp.getActivePresentation();
var slides = deck.getSlides();
var presLen = slides.length;
Logger.log("num slides: " + presLen );
var variables = globalVariables(); //load the Global variables
if (!variables.sheetId) {
Logger.log("SheetId not retrieved from global variables!");
return;
}
try {
var ss = SpreadsheetApp.openById(variables.sheetId);
if (!ss) {
Logger.log("The data spreadsheet does not exist");
SlidesApp.getUi().alert("The data spreadsheet does not exist" + variables.sheetId );
return;
}
} catch (e) {
Logger.log(" in catch for openBYId: " + variables.sheetId );
SlidesApp.getUi().alert(e);
return;
}
I am writing a script that requires an API call be sent to a service that requires OAuth 2.0 authentication.
The code looks as follows, using the apps-script-oauth2 library.
However, the OAuth 2.0 flow never starts, and the function fails on the line const accessToken = service.getAccessToken()
with an error message Error: Access not granted or expired. (line 454, file "Service"). It does not provide information as to what went wrong.
function run() {
let accessToken = initService()
// do tasks requiring access token ...
}
/*
* Create and authorize the service.
*/
function initService() {
var service = getService(SERVICE_NAME);
if (!service.hasAccess()) {
var authorizationUrl = service.getAuthorizationUrl();
var template = HtmlService.createTemplate(
'Authorize. ' +
'Reopen the sidebar when the authorization is complete.');
template.authorizationUrl = authorizationUrl;
var page = template.evaluate();
FormApp.getUi().showSidebar(page);
}
const accessToken = service.getAccessToken()
return accessToken
}
/*
* Callback function for OAuth2 authorization.
*/
function authCallback(request) {
let service = getService(SERVICE_NAME);
let isAuthorized = service.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
/**
* Create a new service with the given name.
*/
function getService(name) {
return OAuth2.createService(name)
.setAuthorizationBaseUrl(AUTHORIZATION_URL)
.setTokenUrl(TOKEN_URL)
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
.setParam('login_hint', Session.getActiveUser().getEmail())
.setParam('approval_prompt', 'auto');
}
I'm having some trouble configuring my Google Apps Script to properly handle the token that comes from the API I'm reaching out to. Everything from what I can tell is compatible.
I am using the Apps Script oAuth2 here.
When I run the below scripts I am able to get to the oAuth screen where i validate on the app, and when it passes the credentials back to google scripts on usercallback i get the below error.
Error: Error retrieving token: {"id":"401","name":"unauthorized","detail":"Unauthorized"} (line 541, file "Service")
My oAuth Script is below:
var CLIENT_ID = '...1';
var CLIENT_SECRET = '...2';
// configure the service
function getYNABService() {
return OAuth2.createService('YNAB')
.setAuthorizationBaseUrl('https://app.youneedabudget.com/oauth/authorize')
.setTokenUrl('https://api.youneedabudget.com/v1/budgets?access_token')
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
.setScope('read-only')
.setGrantType('authorization_code');
}
// Logs the redict URI to register
// can also get this from File > Project Properties
function logRedirectUri() {
var service = getService();
Logger.log(service.getRedirectUri());
}
// handle the callback
function authCallback (request) {
var YNABService = getYNABService();
var isAuthorized = YNABService.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
My Google Sheets script is below
// add custom menu
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('YNAB for Sheets')
.addItem('Authorize','showSidebar')
.addItem('Fetch Budget','FetchBudgets')
.addItem('Reset','reset')
.addToUi();
}
/***************************************/
// Show sidebar for Authorization
function showSidebar() {
var YNABService = getYNABService();
if (!YNABService.hasAccess()) {
var authorizationUrl = YNABService.getAuthorizationUrl();
var template = HtmlService.createTemplate(
'Authorize. ' +
'Reopen the sidebar when the authorization is complete.');
template.authorizationUrl = authorizationUrl;
var page = template.evaluate();
SpreadsheetApp.getUi().showSidebar(page);
} else {
// ...
}
}
function reset() {
getYNABService().reset();
}
function FetchBudgets() {
var YNABService = getYNABService();
var response = UrlFetchApp.fetch('https://api.youneedabudget.com/v1/budgets/default/accounts', {
headers: {
Authorization: 'Bearer ' + YNABService.getAccessToken()
}
});
// ...
}
Upon deeper investigation on this, it seems that the problem is on my fault. I mixed the URLs for client/server side auth.
https://app.youneedabudget.com/oauth/token? - this is the correct token URL.
These are the errors I'm getting:
[13-12-23 22:43:26:376 EST] Page type: FileCabinetPage
[13-12-23 22:43:26:376 EST] File blob: undefined
[13-12-23 22:43:26:383 EST] Error msg: Cannot find method addHostedAttachment((class)).
Here's an excerpt from my code:
function doGet(){
var app = UiApp.createApplication().setTitle("Shipping Label Request Form");
var form = UiApp.createFormPanel();
var panel = UiApp.createVerticalPanel();
var attachment = app.createFileUpload().setId('attachment').setName('attachment')
var button = app.createSubmitButton('Submit').setId("button");
app.add(form);
form.add(panel);
panel.add(attachment);
panel.add(button);
// not needed with formPanel //
// var handler = app.createServerHandler('submitAnnouncement');
// button.addClickHandler(handler); //
return app;
}
// submitAnnouncement changed to doPost()
function doPost(e) {
var app = UiApp.getActiveApplication();
var page = SitesApp.getPageByUrl('https://sites.google.com/...')
var fileBlob = e.parameter.attachment;
Logger.log('Page type:' +page)
Logger.log('File blob:' +fileBlob)
try {
page.addHostedAttachment(fileBlob)
}
catch(e){
Logger.log('Hosted attachment error msg:' +e.message);
}
}
The parameter attachment will not be available to the server handler in your code, in order to fix this you can either use
var handler = app.createServerHandler('submitAnnouncement')addCallbackElement(attachment)
Or you can envelope the attachment and button in a Vertical Panel which must then be put inside a Form Panel and your code should start working.