I'm trying to invoke any of the Google API using "Service account" authorization access. I have downloaded ".pk2" file and activated "URL Shortener API" in Services tab of Google API console. Whenever I try to invoke any API (URL shortener or Adsense). I've got following exception -
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
"error" : "invalid_grant"
}
at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:303)
at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:323)
at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:345)
at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:526)
at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:287)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:836)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:412)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:345)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:463)
Below is code snippet -
HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
JsonFactory JSON_FACTORY = new JacksonFactory();
File privateKey = new File(ReportAdsense.class.getResource("mykey.p12").toURI());
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("my_valid_account_id#developer.gserviceaccount.com")
.setServiceAccountScopes(UrlshortenerScopes.URLSHORTENER)
.setServiceAccountPrivateKeyFromP12File(privateKey)
.build();
Urlshortener service = new Urlshortener.Builder(new NetHttpTransport(), JSON_FACTORY, null).setHttpRequestInitializer(credential).build();
UrlHistory history = service.url().list().execute();
First of all "Service account" will not work for Adsense, since it requires user authorization. Hence for Adsense you should use Oauth 2.0. When you are authorized first time using URL https://accounts.google.com/o/oauth2/token, copy-paste and hardcode your refresh token. Than you can use it to get access token, specify client_id, client_secret and your refresh_token to get new access token. Now access token can be used in your application.
Regarding your error, I have faced with similar issue and spent plenty of time to resolve it. First of all, make sure that you are using valid ServiceAccountId - it should point to email which finishes with "developer.gserviceaccount.com". Make sure, that you specified account scopes and activated services in Google Console API.
I fixed this issue by synchronizing system clock in my machine.
There are a lot of topics with similar error without answers. Even more, some people says, that sometimes it works, sometimes it returns invalid grant. It could work on one machine and fail on another. I don't know if it is system clock issue, but I would avoid using Service Account API, since looks like there are bugs and support would not help you
Related
We've had an application happily running with OAuth 2 security (service account and a p12 key) for a couple of years, updating Google directory information (i.e. telephone numbers, names, etc.) via the directory API. The account (i.e. user#domain.com) was mistakenly disabled, so the updates stopped working. The account has been re-enabled (I have verified this by logging in), but the application is now getting 403 - "Not Authorized to access this resource/api" errors when attempting to perform updates.
I've gone through everything I can think of the in the Google API console, and everything seems okay with the service account. Can anyone think of something obvious that I should check to figure out the problem?
If it helps any, the Java code used to do the build the authentication is:
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(googleServiceAttributes.getServiceAccountId())
.setServiceAccountScopes(serviceAccountScopes)
.setServiceAccountPrivateKey(googleServiceAttributes.getPrivateKey())
.setServiceAccountUser(googleServiceAttributes.getServiceAccountUsername())
.build();
(the account scope being used is "https://www.googleapis.com/auth/admin.directory.user", the client jar being used for the directory API is google-api-services-admin-directory_v1-rev22-1.16.0-rc.jar and the API jar is google-api-client-1.16.0-rc.jar)
Thanks...
Try to use the same API through 'Try It'.
One of the features of our Marketplace app makes use of accessing the user's Gmail account via IMAP. We are using the google-api-java-client and google-oauth-java-client libraries and code similar to this example in the java-gmail-imap project as follows:
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_ID)
.setServiceAccountScopes(Arrays.asList(GMAIL_SCOPE))
.setServiceAccountPrivateKey(PRIVATE_KEY)
.setServiceAccountUser(emailAddress)
.build();
credential.refreshToken();
We are then using code based on the examples at https://code.google.com/p/google-mail-oauth2-tools to make the IMAP connection e.g.
IMAPStore imapStore = OAuth2Authenticator.connectToImap("imap.googlemail.com",
993, emailAddress, credential.getAccessToken(), false);
The majority of the time this appears to work correctly, however we are seeing that for a small but significant number of requests the call to Google made by refreshToken() fails with an HTTP 500 error and an HTML response where the JSON would normally be returned e.g.
<p class="large"><b>500.</b> <ins>That's an error.</ins></p>
<p class="large">The server could not process your request.
<ins>That's all we know.</ins></p>
We were advised by a developer advocate at Google that we refresh tokens are not supported for service accounts and we should be using an approach like in this example.
However, it seems like without the call to refreshToken then accessToken is not populated on the credential object and then this results in a NullPointerException when we call OAuth2Authenticator.connectToImap
From the source for GoogleCredential it did seem like executeRefreshToken() is overridden to handle service accounts i.e. instead of performing a refresh it simply requests a new token, and then this bit of code in Credential then handles populating the access token:
TokenResponse tokenResponse = executeRefreshToken();
if (tokenResponse != null) {
setFromTokenResponse(tokenResponse); ....
We were unsure whether we need to enclose our call to refreshToken() in a retry loop to work around the intermittent 500 errors or whether we need to make other changes to our code to follow the recommended approach for this scenario.
Can anyone advise?
I use the java-gmail-imap example code in production (but it is only used to display an inbox in our University portal, there isn't much interaction that would require me to reuse the same refresh token for instance).
Depending on your usage, I wonder if in your case some kind of throttling is coming into play (I've read in places that Gmail can occasionally throttle access).
Elsewhere I've seen Google APIs talk about making retries using an exponential backoff algorithm.
You have to be a little careful when comparing the usage of OAuth 2.0 with the other Google Service APIs and Gmail. Gmail is special in that it uses XOAUTH2. That said I've seen other Google API's that appear to need the refreshToken call. The documentation is a bit unclear and says things like "Refresh the access token, if necessary" (as you say it doesn't seem to work without this step but I haven't done any experimentation with re-using refresh tokens via credential.setRefreshToken(String refreshToken)).
I'd be interested to hear how you get on.
I am trying to get an access token for Google Service Account. Following is my code -
String SERVICE_ACCOUNT_EMAIL = "edited#developer.gserviceaccount.com";
List scope = new ArrayList();
scope.add("https://www.googleapis.com/auth/admin.directory.user");
String keyFile = "C:\\edited-privatekey.p12";
HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
JsonFactory JSON_FACTORY = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(scope)
.setServiceAccountPrivateKeyFromP12File(new java.io.File(keyFile))
.build();
credential.refreshToken();
String accessTokens = credential.getAccessToken();
Although the code works fine and I do get an access token, when I try to use it to 'GET' a Google Apps User using the Google Directory APIs, I get a 403 - Forbidden response code. Could someone please help?
I know the code for GET user is correct because it works fine with the access token generated by Google Apps Admin.
You need to set an admin account with:
.setServiceAccountUser(some_admin_email)
And make sure your App (with the correct scopes) is granted access in the cpanel.
Proceed to https://admin.google.com . Login and add Security control if not exists from More Controls.
Click on Security->Advance Settings->Manage ThirdParty OAuth Client Access and check that those scopes are added(comma separated) for your xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com service account id/client id.
You have to enable the specific api before using it inside https://console.developers.google.com/ library, to make it work with your api key.
watch the video https://www.youtube.com/watch?v=s_G5CnAu69M.
I'm using Google APIs Client Library for JavaScript (Beta) to authorize user google account on web application (for youtube manipulations). Everything works fine, but i have no idea how to "logout" user from my application, i.e. reset access tokens.
For example, following code checks user authorization and if not, shows popup window for user to log into account and permit web-application access to user data:
gapi.auth.authorize({client_id: CLIENT_ID, scope: SCOPES, immediate: false}, handleAuth);
But client library doesn't have methods to reset authorization.
There is workaround to redirect user to "accounts.google.com/logout", but this
approach is not that i need: thus we logging user off from google account not only from my application, but also anywhere.
Google faq and client library description neither helpful.
Try revoking an access token, that should revoke the actual grant so auto-approvals will stop working. I assume this will solve your issue.
https://developers.google.com/accounts/docs/OAuth2WebServer#tokenrevoke
Its very simple. Just revoke the access.
void RevokeAcess()
{
try{
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/revoke?token="+ACCESS_TOKEN);
org.apache.http.HttpResponse response = client.execute(post);
}
catch(IOException e)
{
}
}
But it should be in asyncTask
It depends what you mean by resetting authorization. I could think of a three ways of doing this:
Remove authorization on the server
Go to myaccount.google.com/permissions, find your app and remove it. The next time you try to sign in you have to complete full authorization flow with account chooser and consent screen.
Sign out on the client
gapi.auth2.getAuthInstance().signOut();
In this way Google authorization server still remembers your app and the authorization token remains in browser storage.
Sign out and disconnect
gapi.auth2.getAuthInstance().signOut();
gapi.auth2.getAuthInstance().disconnect();
This is equivalent to (1) but on the client.
Simply use: gapi.auth.setToken(null);
Solution for dotnet, call below API and pass the access token, doc - https://developers.google.com/identity/protocols/oauth2/web-server#tokenrevoke
string url = "https://accounts.google.com/o/oauth2/revoke?token=" + profileToken.ProfileAccessToken;
RestClient client = new RestClient(url);
var req = new RestRequest(Method.POST);
IRestResponse resp = client.Execute(req);
I'm using Google client libraries and trying to make a GET request to Google Play API.
GoogleCredential credential= new GoogleCredential.Builder().setTransport(netHttpTransport)
.setJsonFactory(jacksonFactory)
.setServiceAccountId(CLIENT_ID)
.setServiceAccountScopes(SCOPE)
.setServiceAccountPrivateKeyFromP12File(file)
.build();
credential.refreshToken();
HttpRequestFactory requestFactory =netHttpTransport.createRequestFactory(credential);
GenericUrl url = new GenericUrl(URI);
HttpRequest request = requestFactory.buildGetRequest(url);
HttpResponse response = request.execute();
I get
{
"code" : 401,
"errors" : [ {
"domain" : "androidpublisher",
"message" : "This developer account does not own the application.",
"reason" : "developerDoesNotOwnApplication"
} ],
"message" : "This developer account does not own the application."
}
My app is unpublished, would that cause the problem?
I've got the same problem. It occurs because you authorize user in Google API who does not own the application and try to get data that belong to your app.
In this topic it is well described. http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=2528691&topic=16285&ctx=topic
You should authorize by OAuth2 the owner of application, and then use Google API with obtained token.
The problem is you are using the Service accounts OAuth 2.0 flow to authorize to the android-publisher API. I was doing it the same way. However, Google requires to use the Web server applications flow, which is ridiculous, since a human interaction is needed to allow for the API access.
Fortunately there is a way around it. You just have to obtain the refresh_token, store that and keep using it for future API calls. I wrote about it in more detail on my blog.
We also struggled with this problem as we wanted to validate a purchase on our servers in order to unlock certain features. We tried multiple solutions and frameworks, written by fellow community users and even official implementations but none worked.
Turns out all we had to do was renew our OAuth token (which we just created) and then it all started working.
I suspect that problem is exactly in publishing. You first need to bind your app to your (developer) account and then you will receive CLIENT_ID and other credentials (such as secret key and so on).