I'm trying to make a standalone Google Apps Script to access an API through the OAuth2 library, found here: https://github.com/googlesamples/apps-script-oauth2
I have not been able to get any of the examples to work so far, but I think it's because I cannot register my application and set the OAuth redirect URI. This is my first attempt at using OAuth.
Here are the steps I've taken:
Create a new standalone Google App Script
Paste in the contents of Dropbox.gs (for this example)
Add the OAuth2 library to my script
(Resources > Libraries, and paste in the ID listed in the how-to)
Go to the console (Resources > Cloud Platform project) and navigate to
the APIs & Services > Credentials page
Grab the Client ID and Client secret from that page and paste them into my script.
Get the redirect URI from the script (by running logRedirectUri())
Paste the redirect URI into the cloud platform console, and hit save.
I get the error shown at this link (which reads "You do not have permission to perform this action.
Request URI error
From what I've studied, I need this URI entered in order to make this script work. Why won't this save?
Here are some things you can try.
Step one
The web app api
Change from using the appscript API to using a web app API. You can create a new one in the API admin console.
Then select the web app option.
Finally you will have a new API.
Open the new API and get the client ID and secrets.
Paste into the api the redirect from your app.
https://script.google.com/macros/d/{SCRIPT ID}/usercallback
Step two
The authentication and redirect
The next bit is tricky and may need some fiddling.
The first part of the code below generates a URL. The user then needs to be directed to open the generated URL in a new window. The new window will show all the usual google permissions including any scopes. When the user accepts they will be redirected back to your app.
function getAuthURL() {
var driveService = getDriveService();
if (!driveService.hasAccess()) {
var authorizationUrl = driveService.getAuthorizationUrl();
Logger.log(authorizationUrl)
}
function authCallback(request) {
var driveService = getDriveService();
var isAuthorized = driveService.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
Step three
The access token
Now the user gave permission it is possible to get the access token and pass it along on with fetch requests.
function makeRequest() {
var driveService = getDriveService();
var response = UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files?maxResults=10', {
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
}
});
// ...
}
Related
I'm using the https://github.com/googleworkspace/apps-script-oauth2 library in a Google Sheet script to call an external API. It works well for the owner of the sheet. For all other users it fails in creating the service/property store? It fails the "Service.hasAccess()" condition.
I suspect I am missing some sort of permissions somewhere. I have given users Edit permissions on the Sheet and have gone through other various gyrations trying to figure this out. I decided to apply this script via a Standard Project.
Scope are applied explicitly in the manifest and all works swimmingly for the sheet owner.
''''''''Google Apps Script, Spreadsheet Script in GCP Standard Project
function authorizeUser() {
var myService = getMyService();
if (myService.hasAccess()) {
>FAILS THIS CONDITION for all except spreadsheet owner
}
}
function getMyService() {
return OAuth2.createService('sky')
.setAuthorizationBaseUrl('https://oauth2../authorization')
.setTokenUrl('https://oauth2.../token')
.setClientId('fee......')
.setClientSecret('Ighh.....')
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
//.setScope('https://www.googleapis.com/auth/drive')
// Below are Google-specific OAuth2 parameters.
.setParam('login_hint', Session.getEffectiveUser().getEmail())
}
>I believe the failure is occurring in OAuth2.gs here
function createService(serviceName) {
return new Service_(serviceName);
}
OAuth2.gs: https://github.com/googleworkspace/apps-script-oauth2/tree/master/dist
Any thoughts?
D M
Apparently the suggested code to validate the service state in the apps-script-oauth2 library is not indicative of whether or not the Oauth process can be completed.
Direct the user to the authorization URL
Apps Script UI's are not allowed to redirect the user's window to a new URL, so you'll >need to present the authorization URL as a link for the user to click. The URL is >generated by the service, using the function getAuthorizationUrl().
function showSidebar() {
var driveService = getDriveService();
if (!driveService.hasAccess()) {
I was able to complete my Oauth process regardless of the state returned by has.Access() . I'm not sure if this is a time sensitive operation or something else is at play. At any rate I was able to proceed and develop a final solution as a GAS web app.
I'm using Apps Script API to run a function with the service account's credential.
I added all scopes required in Rest resource API https://developers.google.com/apps-script/api/reference/rest/v1/scripts/run.
But when i run this script below it failed.
function run(){
var CREDENTIALS = {
"private_key": "Your Private key",
"client_email": "Your Client email",
"client_id": "Your Client ID",
"user_email": "Your Email address",
"api_key": "Your API key"
};
var service = getService(CREDENTIALS.client_email,CREDENTIALS.private_key);
service.reset();
if (service.hasAccess()) {
var url = 'https://script.googleapis.com/v1/projects/[SCRIPT ID]:run';
var body = {
"function": [FUNCTION NAME]
};
var params = {
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
},
method: 'post',
playload : JSON.stringify(body),
contentType: 'application/json',
muteHttpExceptions: true
};
var response = UrlFetchApp.fetch(url, params);
Logger.log(response);
}
else {
Logger.log(service.getLastError());
}
}
function getService(email, privateKey) {
return OAuth2.createService('Service Account')
// Set the endpoint URL.
.setTokenUrl('https://oauth2.googleapis.com/token')
// Set the private key and issuer.
.setPrivateKey(privateKey)
.setIssuer(email)
// Set the name of the user to impersonate. This will only work for
// Google Apps for Work/EDU accounts whose admin has setup domain-wide
// delegation:
// https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
.setSubject([USER EMAIL])
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getScriptProperties())
// Set the scope. This must match one of the scopes configured during the
// setup of domain-wide delegation.
.setScope('https://www.googleapis.com/auth/script.external_request');
}
I've got a 404 Error and I think it comes from the scopes list.
So I can't run a script deployed as an API Executable with the OAuth2.0 token.
Which scopes should I choose to run a function via an HTTP request?
In your run function, for the params object you should have payload not playload.
You want to use Apps Script API with the service account.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Issue and workaround:
Unfortunately, in the current stage, the method of scripts.run in Apps Script API cannot be used with the service account. The official document says as follows. About this, when I tested this, I could confirm that the method of scripts.run in Apps Script API cannot be used with the service account.
Warning: The Apps Script API doesn't work with service accounts.
From above situation, as the workaround, how about using the access token retrieved by OAuth2? In order to use the Apps Script API with OAuth2, it is required to link Cloud Platform Project to Google Apps Script Project. About this, you can see the flow for linking them at here.
Note:
I think that when you use OAuth2, Oleg Valter's comment and TheAddonDepot's answer are very useful.
References:
Executing Functions using the Apps Script API
Linking Cloud Platform Project to Google Apps Script Project
If this was not the direction you want, I apologize.
Added:
You want to make several users run the script as the owner who is you.
From your replying, I could understand like above. When Apps Script API is used for above situation, the credential information is required to give each user. When each user uses the access token retrieved by your credential information, your goal can be achieve. But I cannot recommend this. So in your case, I would like to use Web Apps to achieve your goal. The flow is as follows.
1. Prepare script.
Please prepare your script. For example, in the current stage, you want to make users run a function of myFunction(), please put the following sample script.
function doGet(e) {
var values = e; // When you want to give the values by requesting, you can use the event object like "e".
var res = myFunction(values);
return ContentService.createTextOutput(res);
}
In this case, the GET method is used. When you want to only run the function, you can use this script. When you want to run the function by giving the large data, you can use doPost() instead of doGet().
2. Deploy Web Apps.
On the script editor, Open a dialog box by "Publish" -> "Deploy as web app".
Select "Me" for "Execute the app as:".
By this, the script is run as the owner.
Here, when "Anyone" is set, the script is run as each user. In this case, it is required to share the script to each user. And the access token is required to be used. Please be careful this.
Select "Anyone, even anonymous" for "Who has access to the app:".
In this case, no access token is required to be request. I think that as the test case, I recommend this setting.
Of course, you can also use the access token. At that time, please set this to "Anyone".
Click "Deploy" button as new "Project version".
Automatically open a dialog box of "Authorization required".
Click "Review Permissions".
Select own account.
Click "Advanced" at "This app isn't verified".
Click "Go to ### project name ###(unsafe)"
Click "Allow" button.
Click "OK".
Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
3. Run the function using Web Apps.
This is a sample curl command for executing myFunction with Web Apps. Please set your Web Apps URL. At above settings of Web Apps, each user can access by the following curl command.
curl -GL \
-d "key=value" \
"https://script.google.com/macros/s/###/exec"
When key=value is used as the query parameter like above, at doGet(e), you can retrieve value using e.parameter.key.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
You can deploy the script as a Web App. To do so, go to Publish > Deploy as web app. Set the Execute the app as: field to Me (youremail). This way you can share the script as a browser link, and any user will run the script with your credentials.
You can add a some user interface with a confirmation message so the users know they have successfully executed the script. You can find the documentation in this link.
i want to get an authorized token for my App Script Application for YouTube Analytics.. Currently i am using cGoa Library to get an Authorized token which requires authentication once as provided in this link .. But for security purpose, we don't want to use external script like the so called cGoa Library. Please i need to know any other ways to acquires Oauth2 access token for YouTube Analytics in Google App Script. Our major objective id to have the token once and for all future uses because we want to automatically run the script on a daily basis. So we need something that will works all time without require more authentication. Currently this is the code i use to get token with cGoa Llibrary.
function oneOffScript() {
var options = {
packageName: 'youtube-analytics', // user defined
clientId: '942097727718-t663ep631aqhd75610g4etpcn3foj827.apps.googleusercontent.com',
clientSecret:'Ng_9HJ34hKSPesvRA6hb85KR',
// following method to add Google scopes. Here we are mixing YouTube Data and Analytics (prefixed yt-)
// in the same service allowing calls to both APIs with the same token (we can do this because both are Google APIs)
// More on mixing services http://ramblings.mcpher.com/Home/excelquirks/goa/mutipleconsent
scopes : cGoa.GoaApp.scopesGoogleExpand (['youtube',
'youtube.force-ssl',
"youtubepartner",
"youtubepartner-channel-audit",
"yt-analytics-monetary.readonly",
"yt-analytics.readonly"]),
service:'google' // always this for Google APIs. Ref: http://ramblings.mcpher.com/Home/excelquirks/goa/services
};
// store one off in user properties
cGoa.GoaApp.setPackage(PropertiesService.getUserProperties(), options);
}
function doGet(e) {
var goa = cGoa.GoaApp.createGoa ('youtube-analytics',
PropertiesService.getUserProperties()).execute (e);
if (goa.needsConsent()) {
return goa.getConsent();
}
Please your response is highly appreciated. Thanks
I am facing HTTP 401 errors while trying to call a deployed Apps Script (as a web app, accessible to "anyone") from a second GAS with UrlFetch and a bearer in authorization header. The scripts were working fine for months until around two weeks ago.
Here are two small scripts to reproduce the error.
Script A - Deployed as a web app, accessible to "Anyone".
function doGet(e) {
var params = e.parameter.params;
console.info("Parameters : " + JSON.stringify(e.parameter));
return ContentService.createTextOutput("Success");
}
Script B - Calling the script A via UrlFetch
function callURL() {
var param = {
method : "get",
headers : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
followRedirects : true,
muteHttpExceptions:true,
};
var url = "https://script.google.com/macros/s/<script_A_deployed_url>/exec?param1=test";
var resp = UrlFetchApp.fetch(url,param);
if(resp.getContentText() != "Success"){
console.info(resp.getContentText());
throw resp.getContentText();
}
}
Can you confirm the following points again?
For the client side, are there some functions except for your script of the client side in the project? If there is only the script in the project, the scope for accessing to Web Apps is not enough. The scopes for Drive API are required to be included in the scope of access token.
You can see the current scopes at File -> Project properties -> Scopes.
For example, those are the following scopes.
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive
In my log, when Who has access to the app: is installed as Anyone, I confirmed that from April 11, 2018, it is required to be shared the project to access to Web Apps. This might be due to the update of Google.
Please share the project of Web Apps with users and try again.
For the Web Apps server side, if you set User accessing the web app for Execute the app as:, please authorize the scopes using own browser. This authorization is required to do only one time.
If these were not useful for your situation, I'm sorry.
Tanaike pointed me in the right direction. Apparently, some internal rules recently changed in the authentication mechanism for Apps Script deployed as a web app.
For B script, the default scope with UrlFetch is https://www.googleapis.com/auth/script.external_request, but it looks like we now need at least read access to A script, which means we also need Drive scopes.
In order to achieve that, you can for example have this function in B script to authorize them.
function setScope() {
DriveApp.getRootFolder();
}
I am trying to set up a chron job to connect to the instagram API, grab 'my feed' and download the images. I cannot get past the OAuth 2.0 step. I have already looked at a number of resources including:
How to authorize with oauth 2.0 from appscript to Google APIs? - methods are deprecated and I cannot get the pop up to for the oauth to show up.
https://code.google.com/p/google-apps-script-issues/issues/detail?id=2580 and all the links that follow in the discussion. I cannot figure out how to apply this to work without an html page.
http://www.googleappsscript.org/home/downloading-instagram-photos-to-your-google-drive-using-google-apps-script works well for hash tags, but I would like to be able to get the feed from my user account.
Any help would be greatly appreciated, this is the best I have been able to figure out, if I could get the pop up to work I would be good to go, but I cannot.
function startInstagram () {
var redurl = getCallbackURL(getInstagram);
var consumerKey = '#######';
var consumerSecret = '#######';
var parameters = {
method : 'post',
payload:
'grant_type=authorization_code'+'&client_id='+consumerKey+'&client_secret='+consumerSecret+'&grant_type=authorization_code&redirect_uri='+redurl+'&response_type=token'
};
var token = UrlFetchApp.fetch('https://api.instagram.com/oauth/authorize/', parameters).getContentText();
Logger.log(['token', token]);
}
function getInstagram (vars) {
var res = {};
Logger.log(['get', vars]);
return;
}
function getCallbackURL(callbackFunction) {
var scriptUrl = 'https://script.google.com/d/<ID>';
var urlSuffix = '/usercallback?state=';
var stateToken = ScriptApp.newStateToken()
.withMethod(callbackFunction)
.withTimeout(60*10*5)
.createToken();
return scriptUrl + urlSuffix + stateToken;
}
There is a GitHub repository that describes a library for using OAuth2 in Apps Script. In its README file, it shows how to use the library with Drive API as an example. If you substitute the Instagram API for Drive in the example code, you should be close to what you need.
The Instagram API Authorization docs covers most of what you'll need to do to get an access token -- the steps parallel the instructions in the GitHub library.
You'll need to make sure your script is a registered application with Instagram so you can get the client ID and client secret and so you can set the redirect URI, which will be of the form
https://script.google.com/macros/d/{PROJECT KEY}/usercallback
for Apps Scripts.
If you are only interested in downloading your photos, the basic scope permissions (granted by default) should be sufficient.
Once you have a valid access token, you should be able to make requests from the Instagram API using UrlFetchApp.fetch().