I'm having some seriously weird issues with the Google Drive API.
Bear with me here, there's a lot to explain.
I originally got the API working using one of my Google accounts -- let's call it test#google.com for simplicity. I got the Google Picker UI working and it correctly displays the list of files in my Drive account.
I set it to my APP_ID and I have some server-side logic that uses the official google-ruby-api-client gem to connect to the Drive API using the drive.files.get endpoint. I can then download the file's metadata using this snippet:
require 'google/api_client'
client = Google::APIClient.new
# client ID and secret from Google APIs Console
client.authorization.client_id = "{CLIENT_ID}"
client.authorization.client_secret = "{SECRET}"
client.authorization.redirect_uri = "http://myapp.com/users/auth/gdrive"
# access token stored in database from oauth2 flow
client.authorization.access_token = "TOKEN"
client.authorization.scope = [
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
]
result = client.execute!(
:api_method => 'drive.files.get',
:version => 'v2',
:parameters => { 'fileId' => params[:file_id] })
With the test#google.com account I tried this on, the result comes back with the file's metadata, and I'm able to download the file. All peachy.
The problem comes when I use a different Google account. I can access the files in Picker, but when the request is sent to the server with the fileId using the above, I just keep getting a 404 error, saying that the file doesn't exist. I've also tried to access the file's metadata using the OAuth 2.0 Playground provided by google, but the weird thing is that I still get that error, but I also get the same 404 error when I use the test#google.com account.
The only thing I can really think of is that I submitted a version of my app with all the Google Drive details once, and installed the app via the Chrome Webstore. After I realized it was showing up for everyone as a Drive-enabled app, I promptly removed it and changed the manifest.json file back to its normal, non-Drive enabled state. I did read that you should have the app installed via the Chrome Webstore to work properly, but I also read that that's not the case anymore. Are your installed Chrome Webstore apps stored in Google's cloud somewhere so they know that it was installed for a particular account? Or are webstore apps just installed locally? I'm so confused.
What am I missing here? I haven't been this frustrated with an API in a really long time. Please help!
Thanks to Ali Afshar for the answer -- the solution (for now) is to temporarily use the full scope of Google Drive instead of just the drive.file scope.
e.g. instead of using this scope,
https://www.googleapis.com/auth/drive.file
use this one instead:
https://www.googleapis.com/auth/drive
Related
I am unable to upload a file using a valid access token (the token allows me to list groups, drive items etc.). The error that appears is:
invalidRequest: One of the provided arguments is not acceptable.
Using the following PUT request:
https://graph.microsoft.com/v1.0/groups/{my group id}/drive/items/{my item(folder) id}:/test.txt:/content
using content type: text/plain
I tested this exact same request using the graph explorer https://developer.microsoft.com/en-us/graph/graph-explorer and my office 365 user account and the upload worked, so I though it might be an app permissions thing, however this did not work even after granting absolutely every permission to the app and regenerating the access tokem.
Please help as I would rather use an access token than my user account to perform the uploads.
Thankyou
Oliver
Just add a worked endpoint on my side:
PUT https://graph.microsoft.com/v1.0/groups/my-group-id/drive/root:/test.txt:/content
Request body: The contents of the file goes here.
The content type of request header does not need to be configured.
You need to check your folder path too.
I managed to resolve by creating a new app definition in Azure AD with the same permissions and it worked! Perhaps there was some corruption in my original Azure application - maybe the manifest was corrupt (due to a bug)?
You can skip to the problem below but in short I can auth, upload and get a file list but it doesn't correspond to the gmail account I setup with the service account and google dev console
longer version:
I created a new email account say blah#gmail.com and used the google development console to enable drive, contacts and a few other APIs for my project.
Then I created the service account API keys/secrets and downloaded the client_secrets.json as well.
It took a while but got authentication working with the ruby google-api-client and I can login via rails console.
Problem:
I'm getting file.list but it only has the Getting Started.pdf even though I've created a few docs and spreadsheets on the blah#gmail.com service account.
I can also upload a file but don't see it.
I "think" that it's uploading to Google Apps related account? but I have neither set one up nor do I own domain for the organization I'm setting this up with.
I have no idea where the files are going or uploading or how I can access the drive account I actually did setup with blah#gmail.com
what's going on .
EDIT:
Can i get some code example on how to modify this to impersonate user?
require 'google/apis/drive_v2'
Drive = Google::Apis::DriveV2
#drive = Drive::DriveService.new
scope = 'https://www.googleapis.com/auth/drive'
#drive.authorization = Google::Auth.get_application_default([scope])
#give me list of files
#files = #drive.list_files
puts "Search results:"
#files.items.each do |file|
puts "- File: #{file.title} (#{file.id})"
end
If I have to setup google apps this might be tough since they only have subdomains and I don't have direct access. The type of thing I wish I had been told up front.
I'm trying to call the Google Places API to return a list of restaurants near me. When I call this from a browser it works fine. However, when I call this from inside an iOS App I built using Ionic Framework, it fails with a data NULL, status 0 error.
I researched quite a bit, figured out this was a CORS issue. I looked up Google documentation on how to issue GET requests for iOS App. I used my app's bundle id to create a key and client ID for my app. Using the steps here https://developers.google.com/accounts/docs/OAuth2InstalledApp#overview, I obtained an access_token, which I used as a query parameter just like the docs mentioned. Nothing works. What is going wrong?
URL that works on browser: https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=MyServerApplicationsKey&location=33.85463,-84.35870&rankby=distance&types=restaurant
URL that fails from IOS App: https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=MyiOSApplicationsKey&location=33.85463,-84.35870&rankby=distance&types=restaurant&access_token=MyToken
#plexer
I use the server key to call the URL from the browser. That works, as mentioned. From the App if I use this key, I get the following error:
{"data":null,"status":0,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"url":"sameUrlThatWorksOnBrowser","headers":{"Accept":"application/json, text/plain, /"}},"statusText":""}
The Google Places API uses a Public API Access key, rather than oAuth. The good news is that these are a lot simpler to get started with.
Follow the instructions under the Authentication section on this page to create a new Public API access key, then include that key in the &key parameter for your application.
After clicking the "Create new Key" button, select "Server key". That's a bit weird, since you are calling the API from iOS, but it will work.
** UPDATE **
It truly seems that Google has just screwed every single person on the planet by absolutely requiring user interaction to upload a video. Of course I know, they are free. Exactly what I warned the client years ago about, so I don't need to be reminded. Thank You.
So I would like to try to take this in a different direction and just find a loophole and a workaround to still keep doing what we are doing in spite of Google's complete lack of support or caring in any way about the developers and what they have to deal with.
It would be different if you can actually call a phone number and talk to a human being about YouTube Partner access, but you can more quickly get access to the Illuminati.
OAuth 2.0 is now the only supported authentication method period. It does require user interaction.
But what about that token? Does anybody know how long the token lasts?
If I can obtain a token just once using user interaction and place it in the database, I can automate possibly hundreds or thousands of interactions afterwards.
In other words, I'm trying to turn the user interaction into a speed bump instead of a concrete wall.
If anybody has any examples of obtaining that token, caching it, and using it afterwards, that would be a godsend to me right now.
Thanks for the comments and the help. I'm not surprised that the YouTube Developers Forum just folded and said to come here instead :)
It seems that Google has completely pulled the plug on the existing dashboard.
https://code.google.com/apis/youtube/dashboard/gwt/index.html
That link is now 404'd. Tried from several different browsers on different systems.
Registered under the new Google APIs Console already, but still get the problem.
// Set the authentication URL for this connection object
$authenticationURL= 'https://www.google.com/youtube/accounts/ClientLogin';
// Try to connect to YouTube with the channel credentials passed
try {
$httpClient =
Zend_Gdata_ClientLogin::getHttpClient(
$username = $channelfields['EMAIL_ADDRESS'],
$password = $channelfields['PASSCODE'],
$service = 'youtube',
$client = null,
$source = 'Redacted Data',
$loginToken = $channelfields['CACHED_TOKEN'],
$loginCaptcha = '',
$authenticationURL);
} catch (Zend_Gdata_App_HttpException $httpException) {
$update_error['response_body'] = $httpException->getRawResponseBody();
$update_error['error'] = 1;
} catch (Zend_Gdata_App_Exception $e) {
$update_error['message'] = $e->getMessage();
$update_error['error'] = 1;
}
This code has worked perfectly fine before, but does not work with the older API key, or the newer one generated inside the Google APIs console.
I'm attempting a simple upload and this concerns me greatly:
"The service account flow supports server-to-server interactions that do not access user information. However, the YouTube Data API does not support this flow. Since there is no way to link a Service Account to a YouTube account, attempts to authorize requests with this flow will generate a NoLinkedYouTubeAccount error."
From all reports it seems that Google has forced YouTube uploads to become interactive in all cases precluding all possibility of platforms that automatically upload generated content from working at all.
Any help or insights into the process is appreciated.
P.S - Ohhh, it's been awhile since I looked at that system and Google shut down the YouTube Developer Forums and said "YOU" were responsible for their support now :)
OAuth2 does support the ability to avoid user interaction through the offline access type parameter (ie, using access_type=offline). Check out Google documentation for details.
The solution is really rather simple. Your app needs to use oauth to request offline access. It will be given an access cide which you convert to a refresh token, which is the thing you store in your database. This doesn't expire. Well actually it sometimes does, but that's another story. Whenever you need to access the api, use the stored refresh token to request an access token which you include in each api call.
See https://developers.google.com/accounts/docs/OAuth2WebServer for details.
I don't know what you did but https://code.google.com/apis/youtube/dashboard/gwt/index.html works perfectly fine for me. Maybe it was a temporary issue. If you want no user interaction you HAVE to use YouTube API v2 OR you have to use v3 with methods that don't require authentification OR you have to provide your own youtube account credentials which is not recommended and probably not appropriate for you situation.
Several issues to respond here, I think.
1) The older API console has not been removed, but I've noticed intermittent outages to it and to the newer API console while Google is rolling out their new "cloud console."
2) ClientLogin was officially deprecated in April of 2012, not just 48 hours ago. Jeff Posnick has detailed all the changes over the months (and related ones, such as AuthSub, Youtube Direct, etc.) at his blog (apiblog.youtube.com).
3) You're right that, with v3 of the APIs, you cannot do automatic uploads across the board, as the oAuth2 flow requires user interaction. However, given the limited description of your use case, using refresh tokens is probably your best bet. If the content is user generated, somewhere they must be logging into your app, correct? (so that your app knows which credentials to leverage to do the uploads). At the point they're logging into your app, and you're starting the oAuth2 flow, you just have to hit the first oAuth endpoint and pass it the parameter access_type=offline (along with any other parameters). This will ensure that, when they grant that initial permission, you're returned a refresh token instead of an access token. With that refresh token, you can exchange it for multiple access tokens as needed (an access token lives for about an hour. I don't know how long a refresh token lives, but I've never had one expire before my own login cookies did, and then I just get a new one when my users re-login to my app).
Here's some more info on how to use the refresh token; note, too, that the various google api client libraries make it pretty smooth.
https://developers.google.com/accounts/docs/OAuth2WebServer#refresh
Also, this video tutorial from a Google Developers Live broadcast a couple of months ago might help illustrate the point: http://www.youtube.com/watch?v=hfWe1gPCnzc -- it's using the oAuth playground rather than a client library, but the concept is the same.
The answer is to use google-api-php-client, create an interactive auth page, and set up YouTube API v3 correctly with the new API console.
You can create a very simple page that will authenticate for the supplied channel and then store the correct token in your database. Is already working and uploading hundreds of videos on one channel. You do need to remember to fully activate yourself under the new API console and add the services required. Just keep authenticating and adding the services it says it needs. After that, the regular v3 upload process works just fine. On failure send a group an email and they can get a new token in 10 seconds.
Not the most elegant solution, but the documentation from Google is far from elegant anyways that Stack Overflow is now their front line support.
Just hang in there, a solution is always found. Don't give up!
I didn't get here by myself either, the other answers on this page helped me get all the way to this point. Thanks guys.
P.S - Don't forget the scopes
$client->setScopes("https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtube.upload");
We are trying to use google.picker to have our users upload files to our drive account (i.e., the user is not required to have a Google account to upload).
We're trying to use regular Google accounts as application-owned accounts and got our AUTH_TOKEN using OAuth2 and set it using .setOAuthToken(AUTH_TOKEN) . We followed everything described in the docs.
However, when uploading, we got a Server Rejected error. The call to https://docs.google.com/upload/resumableupload?authuser=undefined returned:
{"errorMessage":{"reason":"REQUEST_REJECTED","additionalInfo":{"uploader_service.GoogleRupioAdditionalInfo":{"completionInfo":{"status":"REJECTED"},"requestRejectedInfo":{"reasonDescription":"agent_rejected"}}},"upload_id":"AEnB2Ur64Gb0JDCk_8mg5EhpdcaqL82wBQHumHjcGvDqYibtksmUzhfhBolsmBFzRuvQPRyi43SYfactJZvIWYrQ6xAqzu3L9g"}}
We know we cannot use service accounts since the picker doesn't support it.
Do we miss something in getting the AUTH_TOKEN? Do we need to something in the console?
Give us a little more code, or check the call to gapi.auth.authorize()
Check that you are using the correct scope to obtain the OAuth token.
Scope should be https://www.googleapis.com/auth/drive
Double-Check the scope declaration:
https://developers.google.com/accounts/docs/OAuth2Login#sendauthrequest
Check the call to gapi.auth.authorize()
window.gapi.auth.authorize(
{
'client_id': clientId,
'scope': scope,
'immediate': false
},
handleAuthResult);
from: https://developers.google.com/picker/docs/#hiworld
Without an actual code sample, it is very difficult to say exactly what is going on. Most likely it is the auth token colection. However, it may also be something as simple as not defining a google User (clientID) which in turn impacts the gapi.auth.authorize() call.
maybe this thread can help you: https://groups.google.com/forum/#!topic/Google-Picker-API/PPd0GEESO78
It is about setting the oauth context
or this one:
https://productforums.google.com/forum/#!msg/drive/GDl4uBkkbxM/jRejcxI-EV8J
It is about the type of file you try to upload with autoconvert on..
Use a Google Apps script on Drive with the function doPost to send data to the server. Then write to file with the Drive API. On publish, you have to set the permissions to "accessible to anyone, even anonomous" if doing cross-domain calls. Make the script run under your user name in Google (for testing), but most likely you would want that function moved onto some application-user account in Gmail.
If you need a level of authentication involved, even if the script is made public, you may authenticate against a CloudSQL hosted database and/or with the Jdbc library to connect to an external resource.
The Scope seems to be the problem.OAuth Token must be obtained using correct scope only:
http://tinyurl.com/ldotq4y
Easily replace scope: 'https://www.googleapis.com/auth/drive.readonly' to scope: 'https://www.googleapis.com/auth/drive' . So that you're allow to make change including upload something to your Google Drive account.