Question

I'm trying to use the Google Drive API through the App Identity interface provided with Google App Engine. This basically allows my web application to communicate with Google's APIs from server to server.

I don't need my users to login, I simply need to display my own Google Drive documents.

However, after I set all the appropriate values and scopes, and enable all the right Google Drive knobs on the console page, I still get this for a simple GET request to https://www.googleapis.com/drive/v2/files:

{ "error": { "errors": [ { "domain": "usageLimits", "reason": "dailyLimitExceededUnreg", "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.", "extendedHelp": "https://code.google.com/apis/console" } ], "code": 403, "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup." }}

What's wrong? What am I missing? Here's the code that actually does the request - funny thing is that it works great if I use other APIs such as the URL shortener API:

var scopes = new java.util.ArrayList();                                                                                                    
scopes.add("https://www.googleapis.com/auth/drive");                                                                                       
var appIdentity = AppIdentityServiceFactory.getAppIdentityService();                                                                       
var accessToken = appIdentity.getAccessToken(scopes);

var url = new URL("https://www.googleapis.com/drive/v2/files");                                                                            
var connection = url.openConnection();                                                                                                     
connection.setDoOutput(true);                                                                                                              
connection.setRequestMethod("GET");                                                                                                        
connection.addRequestProperty("Content-Type", "application/json");                                                                         
connection.addRequestProperty("Authorization", "OAuth " + accessToken.getAccessToken());

EDIT

If I simply change the API to use the urlshortner API for example, it works:

var url = new URL("https://www.googleapis.com/urlshortener/v1/url/history");

And output:

{ "kind": "urlshortener#urlHistory", "totalItems": 0, "itemsPerPage": 30}

So there must be something not working with Google Drive and App Identity?

EDIT 2

I've found some help from the correct answer here: https://stackoverflow.com/a/12526286/50394

But it's talking about setting Client API scopes on Google Apps, and I'm not using Google Apps, I'm simply using Google App Engine's domain foo.appspot.com

Was it helpful?

Solution

The 403 error you are getting means that there was no Authorization header in your GET. The logic is that without an Authorization header, you are anonymous (you are legion blah blah :-)). The Drive quota for anonymous use is zero, hence the message. URL shortener has a higher quota for anonymous so it works.

I suggest you change the URL to point to an http server of your own, and check what headers you are actually sending.

OTHER TIPS

AFAICT you should be using Bearer in the Authorization header.

Probably what's happening is, Drive API doesn't recognize the service account (because of the wrong header?) and thus taking it as an anonymous request since no key parameter wasn't provided either (see common query params).

Try this:

connection.addRequestProperty("Authorization", "Bearer " + accessToken.getAccessToken());

Or you could try adding the token as access_token query param.

I think you should at least setup an API console entry with Drive API enabled at https://code.google.com/apis/console Once you create this you'll get an ID you can use in your GoogleCredential object. From the GoogleCredential object you can get the access token which you can than add to your request.

What I read here (Google drive via service accounts) was that you use a slightly different style that uses an API KEY that you retrieve from the Developer Console.
The pertinent parts for me were to generate a "Key for Server Applications", then use this technique, which I hadn't read anywhere else!

      HttpTransport httpTransport = new NetHttpTransport();
  JsonFactory jsonFactory = new JacksonFactory();
  AppIdentityCredential credential =
      new AppIdentityCredential.Builder(DriveScopes.DRIVE).build();
  // API_KEY is from the Google Console as a server API key
  GoogleClientRequestInitializer keyInitializer =
      new CommonGoogleClientRequestInitializer(API_KEY);
  Drive service = new Drive.Builder(httpTransport, jsonFactory, null)
      .setHttpRequestInitializer(credential)
      .setGoogleClientRequestInitializer(keyInitializer)
      .build();

This answer claims that:

Service Accounts are not supported by the Drive SDK due to its security model.

If that's still true, one workaround is to perform a regular OAuth dance once with a regular Google Account, and persist the access and refresh token in the datastore.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top