Why is GoogleAuth failing to refresh in my webapp? - google-app-engine

I am testing using PyDrive in my web app and the refresh part causes it to return a 500 error. Sometimes it works after awhile but I'm not sure what the conditions are. The same code works locally always. Any idea what could be the issue?
from pydrive.auth import GoogleAuth
gauth = GoogleAuth()
gauth.LoadCredentialsFile("mycreds.txt")
gauth.Refresh()

Update: Although I can't directly view the errors, it appears trying to refresh authentication tokens on a foreign machine is forbidden. An alternative solution was to go into google cloud and create a service account key for my webapp. I downloaded the key as a json and use that file for authentication.
Additionally, it seems the service account has its own google drive account that isn't easy to access with my browser (but I could see/control it using pydrive in a terminal). For convenience, I shared a folder using my personal gdrive account to the service accounts email. So now I have the service account save files to that shared folder and it is easy for me to see and control the contents using my personal account
from pydrive.auth import GoogleAuth
gauth = GoogleAuth()
from oauth2client.service_account import ServiceAccountCredentials
scope = ["https://www.googleapis.com/auth/drive"]
gauth.credentials = ServiceAccountCredentials.from_json_keyfile_name('client_secrets_serviceacct.json', scope)
gauth.Authorize()
drive = GoogleDrive(gauth)
file = drive.CreateFile({'title': 'testing.txt', 'parents' : [{'id' : 'mygdrivefolderid'}]})
file.Upload()

Related

Use path/slug after Web App's base url in Google Apps Script

I'm looking to make the url by adding a path which is something like this below in Google Apps Script:
https://script.google.com/macros/s/APP_ID/exec/fileName.txt
How can I achieve this for Web App service?
I believe your goal as follows.
You want to access to Web Apps using the URL of https://script.google.com/macros/s/APP_ID/exec/fileName.txt.
For this, how about this answer? I think that you can achieve your goal using Web Apps. As a sample case, I would like to explain about this using a sample script for downloading a text file, when an user accesses to https://script.google.com/macros/s/APP_ID/exec/fileName.txt.
Usage:
Please do the following flow.
1. Create new project of Google Apps Script.
Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.
If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.
2. Prepare script.
Please copy and paste the following script (Google Apps Script) to the script editor. This script is for the Web Apps.
function doGet(e) {
const path = e.pathInfo;
if (path == "filename.txt") {
const sampleTextData = "sample";
return ContentService.createTextOutput(sampleTextData).downloadAsFile(path);
}
return ContentService.createTextOutput("Wrong path.");
}
In order to retrieve the value of fileName.txt in https://script.google.com/macros/s/APP_ID/exec/fileName.txt, please use pathInfo.
For example, when you check e of doGet(e) by accessing with https://script.google.com/macros/s/APP_ID/exec/fileName.txt, you can retrieve {"contextPath":"","contentLength":-1,"parameter":{},"parameters":{},"queryString":"","pathInfo":"fileName.txt"}.
In this case, the GET method is used.
3. 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.
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 I recommend this setting for your goal.
Of course, you can also use the access token. At that time, please set this to "Anyone". And please include the scope of https://www.googleapis.com/auth/drive.readonly and https://www.googleapis.com/auth/drive to the access token. These scopes are required to access to Web Apps.
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.
4. Run the function using Web Apps.
Please access to https://script.google.com/macros/s/###/exec/filename.txt using your browser. By this, a text file is downloaded.
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
Updated on February 14, 2023
In the current stage, it seems that pathInfo can be used with the access token. It supposes that the following sample script is used.
function doGet(e) {
return ContentService.createTextOutput(JSON.stringify(e));
}
When you log in to your Google account and you access https://script.google.com/macros/s/###/exec/sample.txt with your browser, {"contextPath":"","parameter":{},"pathInfo":"sample.txt","contentLength":-1,"parameters":{},"queryString":""} can be seen.
In this case, when you access it without logging in Google account, even when Web Apps is deployed as Execute as: Me and Who has access to the app: Anyone, the log in screen is opened. Please be careful about this.
And, if you want to access with https://script.google.com/macros/s/###/exec/sample.txt using a script, please request it by including the access token. The sample curl command is as follows. In this case, the access token can be used as the query parameter. Please include one of the scopes of Drive API in the access token.
curl -L "https://script.google.com/macros/s/###/exec/sample.txt?access_token=###"
By this, the following result is returned.
{"contextPath":"","queryString":"access_token=###"},"pathInfo":"sample.txt","parameters":{"access_token":["###"]},"contentLength":-1}

Flutter: Google Drive: File list always returns me 0

I want to retrieve list of files from a Google drive folder. Authentication happens through Service account. Here is my code to do the same:
final _credentials = new ServiceAccountCredentials.fromJson(r'''
{
"private_key_id": "b5-xxxx-17",
"private_key": "-----BEGIN PRIVATE KEY-----\nMI-xxxxk=\n-----END PRIVATE KEY-----\n",
"client_email": "drive-access#xxxx.iam.gserviceaccount.com",
"client_id": "100000000000",
"type": "service_account"
}
''');
final _SCOPES = [SheetsApi.DriveFileScope, SheetsApi.SpreadsheetsScope];
clientViaServiceAccount(_credentials, _SCOPES).then((http_client) {
DriveApi driveApi = DriveApi(http_client);
driveApi.files.list().then((files) {
print('kind: ' + files.kind);
print('list: ' + files.files.length.toString());
});
My log looks like this:
just: drive#fileList
list: 0
In Google Developers console, Google Drive API is enabled and service account it linked properly (as far as I can check).
But I also got another piece of code which writes some data to a spreadsheet, with hardcoded sheetID and that code is working fine.
Any help on what I am doing wrong here?
Retrieving files using Service account:
The service account is different from your Google account. This means that the Google Drive is also different between Service account and your account. So when the file is retrieved using Service account, please share the files in your Google Drive with the Service account. By this, the files in your Google Drive can be retrieved by the Service account.
Scopes:
In your script, DriveFileScope and SpreadsheetsScope are used as the scopes. DriveFileScope is https://www.googleapis.com/auth/drive.file. The official document says this scope as follows.
View and manage Google Drive files and folders that you have opened or created with this app
By this, in your script, how about modifying DriveFileScope as follows?
DriveReadonlyScope (https://www.googleapis.com/auth/drive.readonly)
DriveScope (https://www.googleapis.com/auth/drive)
Note:
I think that this scope can be also used for your situation.
DriveApi.driveMetadataReadOnlyScope (https://www.googleapis.com/auth/drive.metadata.readonly)
References:
Scopes for Google Sheets API, v4
Scopes for Drive API, v3

Not able to create the file on Google Cloud Storage

I am following the below link.
https://github.com/GoogleCloudPlatform/getting-started-java/tree/master/bookshelf-standard/3-binary-data
I create a new Google Cloud Project and followed the above instructions and all fine on the remote server
I tried using an existing old appengine project (created 4-5 years ago). I get the following error at the given code:
"Caller does not have storage.objects.create access to bucket ..."
storage.create(BlobInfo.newBuilder(bucketName, fileName)
// Modify access list to allow all users with link to read file
.setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(),
Role.READER)))).build(),
fileStream.openStream());
Following is the stacktrace
Uncaught exception from servlet
com.google.cloud.storage.StorageException: Caller does not have storage.objects.create access to bucket asw12.
at com.google.cloud.storage.spi.v1.HttpStorageRpc.translate(HttpStorageRpc.java:189)
at com.google.cloud.storage.spi.v1.HttpStorageRpc.create(HttpStorageRpc.java:240)
at com.google.cloud.storage.StorageImpl$3.call(StorageImpl.java:151)
at com.google.cloud.storage.StorageImpl$3.call(StorageImpl.java:148)
at com.google.api.gax.retrying.DirectRetryingExecutor.submit(DirectRetryingExecutor.java:94)
at com.google.cloud.RetryHelper.runWithRetries(RetryHelper.java:54)
at com.google.cloud.storage.StorageImpl.create(StorageImpl.java:148)
at com.google.cloud.storage.StorageImpl.create(StorageImpl.java:141)
at com.example.getstarted.util.CloudStorageHelper.uploadFile(CloudStorageHelper.java:65)
at com.example.getstarted.basicactions.CreateBookServlet.doPost(CreateBookServlet.java:70
I checked up the Google Service Accounts in my old project and it exists. How do I know, who is the 'Caller'?
If you use the google-cloud libraries from App Engine and don't otherwise specify, you will be acting as your project's app engine default service account. Its name is probably something like your-project-id#appspot.gserviceaccount.com.
To get the service account name, open the Service Accounts page in the console, or check the settings on your App Engine page.

Calling GAS Script published as a Service from GWT

I have created a Google Apps Script doPost script that I have published as a Service, only available to myself (as described in https://developers.google.com/apps-script/guide_user_interfaces#RunDecision, section "Publishing a Script as a Service").
I have now a URL like https://sites.google.com/a/macros/[google apps domain]/exec?service=[service key]
I want to call this service from a Google App Engine GWT application, but I don't know how to manage with authentication.
If selecting the "Allow anyone to invoke this service" then "Allow anonymous access", then I can call this service from AppEngine, but in my case, I absolutely need the authentication.
Do you have any idea how to handle it ?
If you only need to call this script from server to server and both of the endpoints are in your ownership, you could use a shared secret to do so, e.g.
Apps Script:
function doPost(e) {
if(e.parameters.secret != 'mysecret') {
return ContentService.createTextOutput("Nice try!");
}
// your code here
}
and transmit it with the request. If you only share your script with "Anyone having the link" that should provide reasonable security - make sure you never log that request nor include it in an error message however ;)

302 status when copying data to another app in AppEngine

I'm trying to use the "Copy to another app" feature of AppEngine and keep getting an error:
Fetch to http://datastore-admin.moo.appspot.com/_ah/remote_api failed with status 302
This is for a Java app but I followed the instructions on setting up a default Python runtime.
I'm 95% sure it's an authentication issue and the call to remote_api is redirecting to the Google login page. Both apps use Google Apps as the authentication mechanism. I've also tried copying to and from a third app we have which uses Google Accounts for authentication.
Notes:
The user account I log in with is an Owner on all three apps. It's a Google Apps account (if that wasn't obvious).
I have a gmail account this is an Owner on all three apps as well. When I log in to the admin console with it, I don't see the datastore admin console at all when I click it.
I'm able to use the remote_api just fine from the command-line after I enter my details
Tried with both the Python remote_api built-in and the Java one.
I've found similar questions/blog posts about this, one of which required logging in from a browser, then manually submitting the ACSID cookie you get after that's done. Can't do that here, obviously.
OK, I think I got this working.
I'll refer to the two appIDs as "source" and "dest".
To enable datastore admin (as you know) you need to upload a Python project with the app.yaml and appengine_config.py files as described in the docs.
Either I misread the docs or there is an error. The "appID" inthe .yaml should be the app ID you are uploading to to enable DS admin.
The other appID in the appengine_config file, specifically this line:
remoteapi_CUSTOM_ENVIRONMENT_AUTHENTICATION = (
'HTTP_X_APPENGINE_INBOUND_APPID', ['appID'])
Should be the appID of the "source", ID the app id of where the data is coming from in the DS copy operation.
I think this line is what allows the source appID to be authenticated as having permissions to write to the "dest" app ID.
So, I changed that .py, uploaded again to my "dest" app ID. To be sure I made this dummy python app as default and left it as that.
Then on the source app ID I tried the DS copy again, and all the copy jobs were kicked off OK - so it seems to have fixed it.

Resources