I'm trying to use Google APIs with a service account.
Originally I used a normal project account with credentials and I managed to query the APIs. To use a service account, I modified the code to skip the browser auth flow and only use the service account credentials. It seems that the same request that used to work with the normal project credentials now give an error without clarifying much in the message. It goes as follows:
directoryService = new GoogleService().buildDirectoryService();
Users result = directoryService.users().list()
.setCustomer("my_customer")
.setOrderBy("email")
.execute();
public Directory buildDirectoryService() throws GeneralSecurityException, IOException {
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredential credential = GoogleCredential
.fromStream(new FileInputStream("src/main/resources/newest_service_account_creds.json"))
.createScoped(SCOPES);
return new Directory.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME)
.setHttpRequestInitializer(credential).build();
}
Unfortunately the error message is not saying much:
Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Invalid Input",
"reason" : "invalid"
} ],
"message" : "Invalid Input"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1108)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
at GetDrivePermissions.getFiles(GetDrivePermissions.java:35)
at Main.main(Main.java:7)
I repeated the process two times to make sure I didn't miss anything, including creating a new project, enabling the APIs, adding a service account, adding and enabling domain-wide delegation and obtaining a credentials json file, also adding access to the app in App Access Control in the admin UI. But calling
directoryService.users().list().setCustomer("my_customer").setOrderBy("email").execute();
service still returns the above error message.
The error message refers to "Invalid input", but the request used to work before, with normal application credentials. So the problem must lay not in how I call the Directory service but with my authentication, the only difference between the working and the non-working version.
Since this worked with using the project credentials, there must be something wrong with how I use the service account, but I can't find the error even after repeating the whole process two times. Based on the guides I found about service accounts, this is supposed to work. The credential object is created and it looks proper.
What can I look into next?
Thnaks for any insight in advance.
I changed this part back to use the application's own credentials. I have another call that uses the service account credentials and that works, so this is a workaround for now.
Related
Here is my problem.
I'm developping a web app using google app engine in JAVA.
I've searched the web and saw a lot of different answers to my problem, giving various solutions but none of them worked for me I still get:
{
"code" : 403,
"errors" : [ {
"domain" : "global",
"message" : "Insufficient Permission",
"reason" : "insufficientPermissions"
} ],
"message" : "Insufficient Permission"
}
I'm trying to send a mail from my account to other email adresses (from me to me as a test).
I've implemented successfully the sheets API but remembered I had to go first through a consent Screen but now I really don't know how to popup this screen again to validate scopes permanently, Let me explain myself.
I have two credentials files.
for retrieving users infos to confirm sign in through Google, here I use an openID connect URL and there I user get the consent screen.
use Sheets API and Gmail API as "me" to first retrieve datas from my spreadsheets and send mail from my email adress. As told before I've validated the Sheets API but can't get Gmail to work with the same credentials.
I've tested various scopes add another credential file without any success. Please tell me if you need some code.
EDIT
Just deleted my Sheets API credentials also removed my app fto be linked to my account and now I cannot get the consent screen to popup. and I get:
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
"error" : "invalid_grant",
"error_description" : "Token has been expired or revoked."
}
I'm using this code to get credentials
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
// Load client secrets.
InputStreamReader in = new InputStreamReader(new FileInputStream(new File("WEB-INF/credentials.json")));
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, in);
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}
but google documentation says:
Authorization information is stored on the file system, so subsequent
executions will not prompt for authorization.
Where can I delete the stored file system?
Any help is greatly appreciated
After messing around hours and hours I've finally came with an unexplainable solution.
I remove my app from this page
then change my code to
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
to this
return new AuthorizationCodeInstalledApp(flow, receiver).authorize(null);
to make appear consent screen.
then I after validating my app I get a NullPointerException error so I change back the same line to this
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("myemail#gmail.com");
and if it doesn't work (I guess that's a cache problem)
to this
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("me");
don't ask me why but that did work.
Still any more detailed infos about this behaviour is welcome.
My question is similar to this one. However, I am using the API Java Client Library with a service account, making calls to the API from my server.
My code is following this guide, which is very simple. However, I can't seem to get an appropriate error for my request. This is how I build my AndroidPublisher:
val credential = GoogleCredential.fromStream(FileInputStream(
"/path/to/json"
)).createScoped(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER))
androidPublisher = AndroidPublisher.Builder(httpTransport, JSON_FACTORY, credential)
.setApplicationName(packageName)
.build()
Where the JSON is generated from the Developer Console, under Service Accounts. This is how I make my request:
androidPublisher.purchases().subscriptions().get(packageName, "valid-sku", "invalid-token").execute()
My subscription ID is valid but my token is invalid. I expect an error such as "invalid token" in the response. However, what I get is:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Invalid Value",
"reason" : "invalid"
} ],
"message" : "Invalid Value"
}
Is that a generic error because of the invalid token or is it an authentication issue? If it an authentication issue, how do I solve it? If it is an invalid token issue, how am I supposed to know?
Some more information:
I get the same error when trying to make that call from the API Explorer as well (this time using a Client ID and API Key instead of Service Account).
I have not delegated domain-wide access to the service account. Do I have to for some reason?
I can successfully make other calls to the API such as inappproducts.list
From my experiences, if you have HTTP 400 error with Invalid Value then that purchase or subscription is FRAUD.
You can check out Order Id part of those purchases. Probably in the format of XXXXXXXXXXXX.XXXXXXXXXXXX which is wrong and should be GPA.XXXX.XXXXX.XXXXX.XXX
I don't really count the X char number. I just added to show the logic.
In my case the problem was that I was calling:
purchases.products.get
Instead of:
purchases.subscriptions.get
So, the reason that happened was just because the purchaseToken I was using was wrong.
I did not expect that to be the reason as I thought that in the case of an invalid token, I would receive a "token invalid" error (or something similar). As it turns out, the responses given by Google are pretty inconsistent (a 404 could also be given for an invalid token).
Scratched my head for a few hours, ALL my parameters were correct, and then well.. I realized that I was barking up the wrong tree (endpoint)
https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}
is not this
https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}
/purchases/subscriptions/.. vs /purchases/products/..
For all those who run into this problem, 99% of you need to publish the application for internal testers.
Follow this guide: https://support.google.com/googleplay/android-developer/answer/6062777?hl=en
I'm trying to access my own personal Gmail account using the Gmail API with service account credentials and the Java SDK (v1.22.0). I've generated the credentials and saved the json file locally, then tried running this:
public class GmailAPITest {
public static void main(String[] args) throws Exception {
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
HttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
String filepath = "/path/to/my/creds.json";
GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(filepath))
.createScoped(Collections.singleton(GmailScopes.GMAIL_READONLY));
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName("MyApp")
.build();
String user = "me";
ListLabelsResponse listResponse = service.users().labels().list(user).execute();
List<Label> labels = listResponse.getLabels();
if (labels.size() == 0) {
System.out.println("No labels found.");
} else {
System.out.println("Labels:");
for (Label label : labels) {
System.out.printf("- %s\n", label.getName());
}
}
}
I get a 400 response like this:
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Bad Request",
"reason" : "failedPrecondition"
} ],
"message" : "Bad Request"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1065)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
at ie.test.GmailAPITest.main(GmailAPITest.java:34)
I've tried enabling Domain-wide Delegation on the service credentials, and I've tried changing the user "me" to the email address of my account, but it's the same result. I've also looked at a number of questions with similar problems but I haven't found any of them useful. Am I missing something?
The Gmail API is enabled from the developer console and from there I can see all my failed 4xx requests.
I should also mention that I've created another test using Python, which does exactly the same thing using the same service credentials, and it fails with the same 400 error. I've tried creating a new key (json) under the same security credential and tried using that, but it's the same result.
I've been able to get things working, but only by using the alternative oAuth sign-in process i.e. being prompted to allow/deny access from a web browser, which then saves some oAuth access tokens to a file for future user. This is not ideal, because I'm going to have to constantly renew these tokens. The service credential was presumably created to avoid that exact problem. I'm still very interested in finding out why it's not working for me.
Service account domain wide delegation is for G Suite accounts only and requires the G Suite admin to authorize the service account client id to have access to the necessary scopes. If you are working with consumer Gmail accounts (#gmail.com address) or you don't have G Suite admin approval then you should use normal OAuth flow like you have working now.
I have a SSL secured jetty web server whose task is to read/send emails on behalf of the user. For reading pub/sub is preferred, instead of pull. Have followed these steps from developers.google.com
Created a project in console, enabled Gmail api, created service account with google apps domain-wide delegation downloaded both json and p12 private key
Created OAuth cliend-id by selecting web application gave it a name and added authorized redirect URI to one of the web server api end point
Grant publish right to the topic using console api
Created topic in pub-sub and subscribed with an HTTPs endpoint. Manually checked using console API to publish message and received successfully
Now all that left to do is use java client library to utilize service account credentials and talk to gmail api's on behalf of the user (have collected auth & refresh token from the users for the topic)
Steps tried.
Using the private key downloaded (p12) tried creating credential and for sample retrieving labels of the mail
HttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
String serviceAccount = "service-account#myproject.iam.gserviceaccount.com";
Credential credential = GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(serviceAccount)
.setServiceAccountPrivateKeyFromP12File(new File("/path-to-key-file.p12"))
.setServiceAccountScopes(Collections.singleton(GmailScopes.GMAIL_LABELS))
.build();
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, cred).setApplicationName("web-application-name-created-via-console").build();
String user = "me"; //Tried direct email id also
service.users().labels().list(user).execute();
Response,
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Bad Request",
"reason" : "failedPrecondition"
} ],
"message" : "Bad Request"
}
Note: The coding part is tried in local server and not the one which has SSL. I suppose that shouldn't be an issue as i am having all the necessary key files to access api.
Please, let me know if i am missing something.
Initially I was using the OAuth 2.0 client IDs for my web application to fetch messages from a specific gmail account. And then I realized that consent prompting was not at all required for my application and that's when I stumbled upon the google service accounts that eliminated the consent prompting and storing of the authorization tokens. I did attempt the very same code posted above and hit upon the exact same response posted "message" : "Bad Request", "reason" : "failedPrecondition".
To resolve the issue added the .setServiceAccountUser(googleServiceAccountUser) where googleServiceAccountUser is set to my google email id (xxx#gmail.com) from which my application was attempting to read messages. In your code snippet provided the call ".setServiceAccountUser()" is missing and adding it may help you resolve the issue.
private static Credential authorize() throws Exception {
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(googleServiceAccountId)
.setServiceAccountPrivateKeyFromP12File(new File(privatekeyFile))
.setServiceAccountScopes(Collections.singleton(GmailScopes.MAIL_GOOGLE_COM))
.setServiceAccountUser(googleServiceAccountUser)
.build();
return credential;
}
I'm trying to write an AppEngine app that writes a Google Document to Google Drive, puts in a specific set of folders and sets access rights. I have this working with the old DocsList API but since that just got deprecated I decided to update my code (and I had some additional functions to add anyway).
Problem I'm facing is this: When I use a service account and try to impersonate a specific user I get a 403 with usageLimits even though I have not used up any of my quota.
Here is the code I'm using:
GoogleCredential credentials = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("xxxxxxxxxxgserviceaccount.com")
.setServiceAccountScopes(DriveScopes.DRIVE)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File("xxxx-privatekey.p12"))
.setServiceAccountUser("user#xxxxxx.org").build();
I than use these credentials to initiate my Drive object:
Drive d = Drive.builder(httpTransport, jsonFactory)
.setHttpRequestInitializer(credentials)
.setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() {
#Override
public void initialize(JsonHttpRequest request) {
DriveRequest driveRequest = (DriveRequest) request;
driveRequest.setPrettyPrint(true);
}
}).setApplicationName("MYAPPNAME").build();
BTW: I've tried using new Drive(....) but that just won't work, no matter what I try. Keeps throwing errors that internal methods are not found!
Back to this issue:
When I than use 'd' to call something like .files().get("SOMEFILEID").execute() I get a 403
{ "code" : 403,
"errors" : [ {
"domain" : "usageLimits",
"message" : "Daily Limit Exceeded. Please sign up",
"reason" : "dailyLimitExceededUnreg",
"extendedHelp" : "https://code.google.com/apis/console"
} ],
"message" : "Daily Limit Exceeded. Please sign up"
}
I can't figure out why this doesn't work. I've look online all day but can't find a suitable answer. Some help is very much appreciated.
So pinoyyid's answer did help, although it wasn't the definitive answer.
I ended up solving it like this:
I took my AppID (xxxxx.apps.googleusercontent.com) and added it to my CPanel https://www.google.com/a/cpanel/[[YOURDOMAIN]]/ManageOauthClients
with these scopes:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
After that I had could do an authenticated request to the Drive environment. Running into a stack of different issues now but the 403 got solved.
I usually get a 403 when the API call was missing the Authorization http header. Trace the http and look at the headers. The rationale for the "quota" message is that without an Auth header, you are anonymous, and the quota for anonymous use is zero.
You might also check that your app is registered for both of the Drive APIs, as I've heard that that can cause the same problem.
On the internal method issue, that sounds like you're using incompatible library versions. What worked best for me was to delete all of the libraries I had downloaded with the sample code, then use the Google Eclipse plugin to "Google/Add Google APIs..." to download all of the latest versions.