Form based (Cross Domain) Google Drive API Upload with caveats - google-api

I'm currently working on a rather interesting... project. I have a client who wants to allow form uploads (from a page presented on their server) specifically to their own google drive account. The platform being used is essentially LAMP.
Single (pre-authenticated) google drive account. Multiple otherwise anonymous upload sources (users).
They do not want users to be required to have their own google accounts (rules out simply using Picker on the user's own drive files).
They want some degree of backwards browser compatibility, such as IE8 (rules out XHR to form the post using HTML5's file API to read the filedata). They don't want to use flash/etc due to potential compatibility issues with certain mobile browsers.
What is working:
Authenticating (getting a refresh token, storing, using it to get access tokens as needed)
Uploading a file to the account without metadata
Result of file upload being sent to hidden iframe
Catching the iframe load event via jquery to at least know something has happened
Problems:
The REST API upload endpoint does not support CORS: there is no way to access the result iframe directly. (see: Authorization of Google Drive using JavaScript)
The return from a successful upload is only raw JSON, not JSONP.
There is seemingly no way to host anything with proper headers to open via browser on the googleapis.com domain, so easyXDM and similar multi-iframe with cross origin workaround communication javascript approaches are ruled out.
There is no way to embed a callback URL in the POST from the submit, the API does not allow for it.
The Picker displays errors on trying to upload if you pass it an Oauth2 token that is not for a user who is also authenticated in their browser (assumedly via cookie). Strangely enough you can show files from the Oauth2 token's matching account, but other than in a browser instance where the target Oauth2 token's account matches the already logged in user any file uploads fail with an ambiguous "Server rejected" message. This happens with all files and file types, including files working in an authenticated browser instance. I assume it's an authentication flow/scope issue of some sort. I haven't tried diving the Picker source.
All of the javascript Google Drive API upload examples seem to rely on using HTML 5 to get the file data, so anything of that nature seems to be ruled out.
While files are uploaded, there's no way other than guesstimating which file came from which user, since we can't get the file object ID from the result in our inaccessible iframe. At best we could make a very rough time based guess, but this is a terrible idea in case of concurrency issues.
We can't set the file name or any other identifier for the file (not even a unique folder) because the REST API relies on that metadata being sent via JSON in the post request body, not via form fields. So we end up with file objects in the drive with no names/etc.
We can't create the file with metadata populated server side (or via jquery/XHR, or the google javascript API client) and then update it with a form based upload because the update API endpoint exclusively works with PUT (tested).
We can't upload the files to our local server and then send them to google (proxy them) as the php ini is locked down to prevent larger file uploads (and back to restrictions imposed on using HTML5 or flash for why we can't chunk files/etc).
All of this has been both researched and to varying degrees tried.
At the moment this is going on hold (at least it was a useful way to learn the API and gain a sense of its limitations) and I'm just going to implement something similar on dropbox, but if anyone has any useful input it would be lovely!
e.g. is there any way to get this working with Drive? Have I overlooked something?
I also realize that this is probably essentially a less than intended use-case, so I'm not expecting miracles. I realize that the ideal flow would be to simply allow users to upload if necessary to their own google drives and then have them grant file access to our web app (via Picker or API+our own UI), but this becomes a problem when not all of our own users are necessarily already google account users. I know that google would OBVIOUSLY prefer we get even more people to sign up with them in order to have this happen, but making people sign up for a google account to use our app was ruled out (not out of any prejudice on our part, it was just too many added steps and potential user hurdles). Even simply having them sign in to google if they did have accounts was deemed unwanted for the basic LCD feature functionality, although it's likely to be added as an additional option on top of whatever becomes the base solution.

The biggest problem with the approach you described is you're introducing a big security issue. Allowing an anonymous user to directly upload to Drive from the client requires leaking a shared access token to anyone who comes by. Even with the limited drive.file scope, a malicious or even slightly curious user would be able to list, access (read/update/delete!) any file that was uploaded by that app.
Of course a public drop box feature is still useful, but you really need to proxy those requests to avoid revealing the access token. If your PHP environment is too restrictive, why not run the proxy elsewhere? You can host a simple proxy to handle the uploading just about anywhere -- app engine, heroku, etc. and support whatever features you need to ensure the metadata is set correctly for your app.

Related

Debug redirect_url in oauth2 flow

We are using Go server side code to interact with Google Ads REST API.
Namely, we authenticate it with help of "golang.org/x/oauth2" and
"golang.org/x/oauth2"packages.
In May (and recently again) we've got a email from Google regarding deprecation
Out-of-band flow, essentially rewording of this
one.
But additionally to common information, Google email listed account, which we
are using to authenticate, as being used in OOB flow and going to be blocked.
We checked our sources and available sources of mentioned packages but was not
able to find redirect URIs which are said to be used for OOB flow as one of
those:
redirect_uri=urn:ietf:wg:oauth:2.0:oob
urn:ietf:wg:oauth:2.0:oob:auto
oob
We explicitly use http://localhost in our code and long-live refresh token
(which seems never expires).
We also tried to use tcpdump to monitor our API calls, but was not able to
learn much from it, because calls are made via https and, therefore, encrypted.
We considered to use man-in-the-middle kind of proxy like
https://www.charlesproxy.com/, but haven't tried it yet, because it become
non-free and because of complexity of setup.
We tried to log our requests to API endpoint with custom RoundTripper, but
have not spotted anything suspicious. It seems that we're using refresh token
only and exchange of code to refresh token just never happen in the code.
Because of this, we don't think that further logging or monitoring with
decrypting https packages may help (but we open to suggestions how to do it better).
Finally, we decided to create a new OAuth 2 Client in the Google console with
fresh set of client id, client secret and refresh token. We obtained a new
refresh token with oauth2l and replaced
credentials in our configuration. But still, we are not sure that new account
will not be blocked by Google due OOB deprication, because seemingly it looks
the same as old one.
Questions:
Why may Google mark our account as OOB?
How can we ensure that newly created account will not be blocked?
Same here.
I find out an answer, that says "Desktop" type of Credentials uses OOB by default. Probably you need to create new Credentials with type "Web"

How to upload/download files directly from Google Drive to the browser without using server bandwidth?

I want to make a web app where the only cost to me is serving the webpage to the user, and all their data is saved to their Google Drive so I don't have to pay for storage or bandwidth.
Is this possible using Google Drive?
I can't see how:
If I want to save something directly from the browser, it needs my application's API key, and I can't put that in the HTML as it is non-secure.
If I try to do anything where the webpage calls my server, the file will have to pass through my server to get to Google.
If I want to save something directly from the browser, it needs my application's API key, and I can't put that in the HTML as it is non-secure.
You need client credentials, api key will only give you access to public data and wont give you the ability to write anything. Web credentials if configured properly are bound to the domain that they are intended to be use for there for they are considered secure.
If I try to do anything where the webpage calls my server, the file will have to pass through my server to get to Google.
Well this is true considering that your server is running the code. There is no way to route directly from the client to the user. Unless you did this with javascript in which case the code is running client sided and running in the users browser.

How to give my application access to a publicly shared Google Drive file

I'm writing an application that downloads publicly shared Google Drive files. My application already knows the IDs of the files it needs to download. Here are three possible ways I can do this, and none of them are satisfactory:
1.) Downloading the file using the url https://drive.google.com/uc?id=<fileID>&export=download works, but is subjected to a severe rate limit restriction because the request is sent with no authentication.
2.) Downloading the file with https://www.googleapis.com/auth/drive.file authentication doesn't work. This is because the documentation says it only provides access to "files created or opened by the app".
3.) Downloading the file with https://www.googleapis.com/auth/drive authentication works (without any other code modifications from the "drive.file" case). I can't use this option because of the required security assessment for applications that use this scope.
The only way forward seems to be using "drive.file" authentication, where these files are opened by the app. How should I do this? I think the Picker API is the intended way to do this from inside an application, but I don't see support for filtering by a specific file ID, and I would prefer a solution that doesn't require user input, especially since options (1) and (3) don't require user input.
(as a side note, the logic behind this security policy doesn't make sense to me. If (2) is not possible because of a security/authentication concern, then (1) should not be possible. If (2) is not possible because of a rate limit concern, then (3) should not be possible. I have been told that this is intended behavior.)
I understand that you are developing a web app that downloads a publicly shared Drive file. If my assumption is correct, then you only need to implement an OAuth 2.0 protocol. You can follow the basic steps as a reference. Here is a more in depth guide for web apps. Please, ask me any question if you have doubts about this approach.
UPDATE
Based on this IssueTracker answer from Google, this is expected behaviour. That is, you need to open the public file in your app first and then you would be able to download it.

Google javascript api client, automatically login to same account without popup

Is there a way to automatically authenticate the google javascript api client, without user interaction?
Something like this:
User loads webpage -> webpage automatically signs in into a predefined user account -> api calls get executed
Basically i want to prevent the popup where you have to select an account and sign in to it. As the account which will be signed in is always the same.
EDIT:
pinoyyid answer looks promising and is what im looking for. But this only works if the user has signed in with an account at least once, if im not mistaken.
Now i dont want to use an account supplied by the user, but a predefined account which i am the owner of and sign this account in.
Im not entirely sure if this is even possible, as i have to provide the password/some authentication code to google and somehow do this in a secure way.
Use Case: The website will create a Youtube Broadcast via the Youtube Data/Livestream API for the specified account.
Yes you can do that. Referring to https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow
there are three pieces of information that will get you where you want to be:-
The OAuth URL can include a login_hint which is the email of your intended user
The OAuth URL can also include prompt=none which will do its work silently
This all needs to run in an iframe because this is based on origins and redirects.
==EDIT==
If the requirement is for a browser client to connect to a Google Account other than that of the browser user, then this is not possible. It's kinda obvious really that to do so would require a credential in the browser which by definition is not a secure environment.
The approach I would take would be to use a service such as Lambda or Google Cloud Functions (or whatever marketing name they have this week) to create a proxy for the corresponding Google API using a credential stored server-side.

Bypass reCAPTCHA from a specified origin

We have a page on our site that uses Google's reCAPTCHA before allowing the user to download a file.
It works great and we totally stopped all the evil bots from spamming our servers.
Now we want to allow a specific entity (user, domain, whatever) to be able to automatically download files without solving the challenge. Or maybe solving it once per session (which will be longer than 2 minutes) and not once per file.
Is there some way we can issue them a multi-use token or have them get a token from Google that will allow them (temporary?) unfettered access to our file downloads? Can we whitelist their domain in the Google admin settings?
Or is this something I need to build myself?
EDIT: It turns I didn't get all the requirements for this assignment. Whitelisting will not satisfy the requirements since it is apparently multiple entities, and that will indubitably change in the future.
reCAPTCHA does not provide specific whitelisting for users or domains.
Instead, you should be looking at making this dynamic on your side. For example, disable reCAPTCHA for signed-in users or generate a token on your server with an expiry time, set that as a cookie on the client, and disable reCAPTCHA for valid tokens.

Resources