Using AppIdentityCredential with Google Drive API - google-app-engine

I am using Google App Engine Identity to access Google Drive API of a Google Apps user.
I have inserted GAE URL in Manage API client access in Google Apps, with the right scope.
AppIdentityCredential credential = new AppIdentityCredential(Arrays.asList(DriveScopes.DRIVE_FILE));
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
Drive service = new Drive.Builder(httpTransport, jsonFactory, credential).build();
I get this error:
com.google.appengine.repackaged.org.apache.http.impl.client.DefaultRequestDirector handleResponse
WARNING: Authentication error: Unable to respond to any of these challenges: {authsub=WWW-Authenticate: AuthSub realm="https://www.google.com/accounts/AuthSubRequest" allowed-scopes="https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/docs,https://www.googleapis.com/auth/drive.file,https://www.googleapis.com/auth/drive.readonly,https://www.googleapis.com/auth/drive.metadata.readonly"}
An error occurred: com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 OK
{
"code" : 401,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError"
} ],
"message" : "Invalid Credentials"
}
Is it necessary to enable GAE application in API Client access in Google Apps? If not, how I authorise GAE app to gain acess to an API Scope?
Is possible to test GAE Identity in Local / Eclipse?
Summarizing, what is wrong or lack?

You can not debug AppEngine Credentials locally. The only way to debug is to use different credentials (AppEngine for production and Key-based service account locally). It will require to write different code for local and production environment.
You need to add your appengine service accounts to Team. If you use custom Google Apps domain for your project than you will need to create email group in your custom domain and add this group. All service accounts should be added to this group.

Consider adding the scopes it tries to authenticate against.
I had the same error in my code and ended up solving in by adding the drive.file scope.

Two important steps
Get the Application Default Credentials Service account JSON file.
Set GOOGLE_APPLICATION_CREDENTIALS environment variable with the path of the JSON file. Example: GOOGLE_APPLICATION_CREDENTIALS=c:/myFolder/my.jsonfile
FYI:
https://developers.google.com/identity/protocols/application-default-credentials

Related

Allowing access to the Google Admin SDK Directory API in Python

I'm trying to setup a google group for marketing purposes, in which when certain users sign up to my application, I send their email to this google group with the following code
# google_admin_apis.py
def add_member(member):
if not member.email:
return False
try:
service = build('admin', 'directory_v1')
except DefaultCredentialsError: # For developers
return False
group_key = 'mygroup#mydomain.com'
body = {
"email": member.email
}
members = service.members()
request = members.insert(groupKey=group_key, body=body)
response = request.execute()
return True
My application is hosted on Google App Engine, so by default ADC will use the default service account when run on the server. I have tried to run this code locally by using gcloud auth application-default-account login and logging in with my G Suite admin account, and also my personal account (both are owners of the GCP project). After this failed, I did some research and realised that to enable OAuth2 to access my G Suite User data (I'm not really accessing anything by inserting a user?!?) I had to 'enable domain wide delegation' on the default service account, so I did this, I then downloaded the service account JSON and attempted to manually authorise with $GOOGLE_APPLICATION_CREDENTIALS, but was still getting a 403. I then went one step further and followed these instructions. Giving my Client ID access to https://www.googleapis.com/auth/admin.directory.group and group.member.
After all this, I still get a 403 error.
With the application-default-credentials I get:
<HttpError 403 when requesting
https://www.googleapis.com/admin/directory/v1/groups/groupKey/members?alt=json
returned "Insufficient Permission">
When using the app engine default service account through .json with either activate-service-account or through the GOOGLE_APPLICATION_CREDENTIALS, I get:
<HttpError 403 when requesting
https://www.googleapis.com/admin/directory/v1/groups/groupKey/members?alt=json
returned "Not Authorized to access this resource/api">
(groupKey intentially censored)
In short, I have an app-engine default service account with domain wide delegation and have given it's client ID access to both roles required for the Directory API's member.insert() function, yet I am still not allowed to call the API as above.
Any help would be greatly appreciated.
I followed this tutorial https://developers.google.com/admin-sdk/directory/v1/quickstart/python to run a similar function locally using Google's google_auth_oauthlib to set up OAuth2 credentials
service = build('admin', 'directory_v1', credentials=creds)

Unable to delete AppEngine Service instance using App Engine Admin API

I am attempting to programmatically delete an App Engine (Standard) backend instance. The code for deleting it runs as a Servlet on another module/service of the same project.
I am able to list the instance and all the details, but attempting to delete it throws the 403 error.
{
"code" : 403,
"errors" : [ {
"domain" : "global",
"message" : "Request had insufficient authentication scopes.",
"reason" : "forbidden"
} ],
"message" : "Request had insufficient authentication scopes.",
"status" : "PERMISSION_DENIED"
}
I am using the App engine default service account:
AppIdentityCredential credential = new AppIdentityCredential(Arrays.asList(AppengineScopes.APPENGINE_ADMIN));
Appengine engine = new Appengine.Builder(new UrlFetchTransport(), new JacksonFactory(), credential).setApplicationName("AppEngineManager").build();
//list all instances - this works fine
//now delete an instance
engine.apps().services().versions().instances().delete(appId, serviceId, version, <instanceid>).execute()
The default App engine service account has Editor role (I also tried manually adding App Engine Admin role, to no avail)
The service account name printed before running the code is also correct. What could be wrong?
This solved it!
AppIdentityCredential credential = new AppIdentityCredential(Arrays.asList(AppengineScopes.**CLOUD_PLATFORM**));
I was using the more obvious looking AppengineScopes.APPENGINE_ADMIN, but this doesn't work. Wish Google would improve their documentation. Had sleepless nights because of a production issue which required us to delete the instances programmatically to solve it.

Get image from Google Cloud Storage in App Engine

I am trying to get an image in my App Engine backend and every time I try to get it I get the following error
com.google.api.client.googleapis.json.GoogleJsonResponseException: 503 Service Unavailable
{
"code": 503,
"errors": [
{
"domain": "global",
"message": "java.io.IOException: The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.",
"reason": "backendError"
}
],
"message": "java.io.IOException: The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information."
}
Now it was my understanding that when making a request from App Engine backend that the Application Default Credentials was sufficient enough to do it.
The Application Default Credentials provide a simple way to get
authorization credentials for use in calling Google APIs.
They are best suited for cases when the call needs to have the same
identity and authorization level for the application independent of
the user. This is the recommended approach to authorize calls to
Google Cloud APIs, particularly when you're building an application
that is deployed to Google App Engine or Google Compute Engine virtual
machines.
taken from here
This is how I am trying to get the image using the Java API
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredential credential = GoogleCredential.getApplicationDefault();
if(credential.createScopedRequired()){
credential = credential.createScoped(StorageScopes.all());
}
Storage.Builder storageBuilder = new Storage.Builder(httpTransport,new JacksonFactory(),credential);
Storage storage = storageBuilder.build();
Storage.Objects.Get getObject = storage.objects().get("myBucket", name);
ByteArrayOutputStream out = new ByteArrayOutputStream();
getObject.getMediaHttpDownloader().setDirectDownloadEnabled(false);
getObject.executeMediaAndDownloadTo(out);
byte[] oldImageData = out.toByteArray();
out.close();
ImagesService imagesService = ImagesServiceFactory.getImagesService();
Image oldImage = ImagesServiceFactory.makeImage(oldImageData);
Transform resize = ImagesServiceFactory.makeResize(width, height);
return imagesService.applyTransform(resize, oldImage);
am I just using the credentials wrong or can I not use the application default credentials?
If you want to access your Google Cloud Storage data from App Engine. You should be using the Google Cloud Storage Client Library
Github Project

Google Cloud Pubsub authentication error from App Engine

We're having trouble publishing messages to a Google Cloud PubSub topic on Google AppEngine. Using the Application Default credentials works perfect locally. But once it's deployed on Google AppEngine it gives the following error:
<HttpError 403 when requesting https://pubsub.googleapis.com/v1/projects/our-project-id/topics/our-topic:publish?alt=json returned "The request cannot be identified with a project. Please pass a valid API key with the request.">
I would assume that it's will use the service account of app engine to access the PubSub API. Here is the code we used to create the credentials.
credentials = GoogleCredentials.get_application_default()
if credentials.create_scoped_required():
credentials = credentials.create_scoped(['https://www.googleapis.com/auth/pubsub'])
http = httplib2.Http()
credentials.authorize(http)
pubsub_service = build('pubsub', 'v1', http=http)
The error is thrown when publishing the actual message to PubSub.
pubsub_service.projects().topics().publish(topic="projects/out-project-id/topics/out-topic", body = { 'messages' : [ { 'data': base64.b64encode(request.get_data()) }]}).execute()
Not that the same flow works doing API call's to "BigQuery", so it's not a general Google API problem. It seems to be specific to PubSub...
It's a rare case of the service account without project id embedded in it. We fixed your service account and you should be good to go now. Sorry for the trouble.

The resource 'projects/<my project>' was not found" error when trying to get list of running instances

My goal is to test out google's orchestrator and the compute engine api by first retrieving a list of active instances. The orchestrator project including the servlet file is stored in a jar.
I'm trying to test out the java google compute engine client api. I have a cron job which calls on the orchestrator servlet. The target for the cron is a backend. From which I try to get the list of instances:
...
AppIdentityCredential credential = getCredential(computeScope);
String appName = ConfigProperties.getInstance().getGceConfigProperties().get("projectId");
try {
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
final Compute compute = new Compute.Builder(
httpTransport, JSON_FACTORY, credential).setApplicationName(appName)
.build();
logger.info("================== Listing Compute Engine Instances ==================");
Compute.Instances.List instances = compute.instances().list(projectId, zone);
InstanceList list = instances.execute();
if (list.getItems() == null) {
logger.info("No instances found. Sign in to the Google APIs Console and create "
+ "an instance at: code.google.com/apis/console");
} else {
for (Instance instance : list.getItems()) {
logger.info(instance.toPrettyString());
}
}
...
There error response I get is(I omitted my project name from the response, I confirmed that I'm using the correct project id in my code):
com.google.cloud.solutions.sampleapps.orchestration.orchestrator.server.GceClientApiUtils
getInstances: com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 OK
{
"code" : 404,
"errors" : [ {
"domain" : "global",
"message" : "The resource 'projects/<project-name-here>' was not found",
"reason" : "notFound"
} ],
"message" : "The resource 'projects/<project-name_here>' was not found"
}
I've also attempted this by retrieving an access token and making a RESTful call to get the list of instances and i received the exact same response. I confirmed the Url constructed was correct by comparing it against a successful query of the instances using the api explorer.
EDIT: I determined the solution to the issue with help of another post:
I was finally able to find the solution in the post Compute Engine API call fails with http 404
I needed to add my app engine service account as a team member with edit capabilities, which it does not have by default. Once I did this, the code worked as expected. I had to do this through cloud.google.com/console, as if done through appengine.google.com, a pending status will be given to the service account and will not have access.
For me i had to make sure i had authorization. Try this in the terminal gcloud auth login
Make sure you are in the right project, you can run this command on your vm to see if you are in the right project:
gcloud config list
Take a look at this post in Google Groups
Do you have access to the developers console https://console.developers.google.com?
It seems that the user account #appspot.gserviceaccount.com has not access to compute engine. In my case I see #developer.gserviceaccount.com.
If you don't have one, visit https://developers.google.com/console/help/new/#generatingoauth2 to create a new Client ID

Resources