My Business
4.1K members online now
4.1K members online now
For developers who are using the Google My Business API to manage locations
Guide Me
star_border
Reply

Service Account - API randomly fails with "401 Unauthorized" error

[ Edited ]
Visitor ✭ ✭ ✭
# 1
Visitor ✭ ✭ ✭

Hi,

I am using a service account to impersonate an account and send various simple requests using the Java client library. The API sometimes responds with valid results, but about half the time it fails with a 401 error. The project has been whitelisted for My Business API usage, and the impersonated user account has given their user consent.

Here is my code for initializing the credential and MyBusiness instance:

        httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        jsonFactory = JacksonFactory.getDefaultInstance();

        GoogleCredential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport)
                .setJsonFactory(jsonFactory)
                .setServiceAccountId(SERVICE_ACCOUNT_ID)
                .setServiceAccountPrivateKey(SecurityUtils.loadPrivateKeyFromKeyStore(SecurityUtils.getPkcs12KeyStore(), Main.class.getResourceAsStream(P12_FILE_NAME), STORE_PASS, ALIAS, KEY_PASS))
                .setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/plus.business.manage"))
                .setServiceAccountUser(IMPERSONATED_USER)
                .build();

        mybusiness = new MyBusiness.Builder(httpTransport, jsonFactory, credential).setApplicationName(APPLICATION_NAME).build();

The code will randomly succeed or fail when executing the following request (just an example, it does so with other types of requests too):

ListBusinessCategoriesResponse catResp = mybusiness.categories().list().setRegionCode("us").setSearchTerm("atm").execute();

The error:

Exception in thread "main" com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized
	at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
	at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
	at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
	at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:384)
	at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
	at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:217)
	at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:868)
	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)

Used Java library: google-api-services-mybusiness-v3p2-java-rev20170110.jar

Maven dependencies:

    <dependencies>
        <dependency>
            <groupId>com.google.http-client</groupId>
            <artifactId>google-http-client</artifactId>
            <version>1.22.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.api-client</groupId>
            <artifactId>google-api-client</artifactId>
            <version>1.22.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.oauth-client</groupId>
            <artifactId>google-oauth-client</artifactId>
            <version>1.22.0</version>
        </dependency>
    </dependencies>

Am I doing something wrong?

1 Expert replyverified_user

Re: Service Account - API randomly fails with "401 Unauthorized" error

[ Edited ]
Google Employee
# 2
Google Employee

Hi @Oleg G,

 

The 401 UNAUTHORIZED error usually occurs due to invalid credentials, so please make sure your application store the refresh token for future use and use the access token to access the Google My Business API. Since an access token has a limited lifetime and once the access token expires, your application should use the refresh token to obtain a new one.

 

You can obtain different refresh tokens for different logged in Google Accounts to access to their respective Google My Business data. I suggest you check out this Accepted Solution for using a single set of OAuth 2.0 credentials for separate Google Accounts. Keep in mind that you can also save the refresh tokens in other persistent data storage such as a database so that you can access your clients’ Google My Business data using the appropriate refresh tokens conveniently across multiple servers. This process requires human interaction to authorize your app during OAuth 2.0 flow only once for each Google Account that you need to access the Google My Business data from.

 

I hope this helps!

 

Thanks,

Shalini

Service Account - API randomly fails with &quot;401 Unauthorized&quot; error

Visitor ✭ ✭ ✭
# 3
Visitor ✭ ✭ ✭

Hi @Shalini S,

 

Thank you for the response! It makes me a little confused, though. I thought the purpose of using the Java client library was that it would handle all the token transactions and refreshing by itself? Do I still need to do things like storing and refreshing the tokens myself? If so, why does the Unauthorized error only appear sometimes and not always?

 

I've done some more testing to try and narrow down the issue... I find that if I have multiple API calls in a sequence in a single program (for example, 10 calls to list all locations, performed in a row) - the very first request sometimes fails with a 401 Unauthorized error, but the ones after it always seem to be successful. I assume the client library is doing something in the background after each call, so that the next one would be successful.

 

Generally, this is not a big problem if I can just repeat each individual request until it succeeds, but I'm wondering if that really is the right approach.

Re: Service Account - API randomly fails with "401 Unauthorized" error

Visitor ✭ ✭ ✭
# 4
Visitor ✭ ✭ ✭

Hi @Shalini S,

 

The problem has escalated. There have been no changes to the code, but today Google API responds to every request with 401 Unauthorized errors. Before this, the first request in a session would sometimes fail with the same error, but all requests after that would always succeed. This time, not a single request has been successful so far. I don't know why this is happening.

 

Like before, I am using the Service Account approach and the latest Java client library. The impersonated account has gone through the User Consent procedure.

 

Creating a GoogleCredential object:

 

GoogleCredential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport)
                .setJsonFactory(jsonFactory)
                .setServiceAccountId(SERVICE_ACCOUNT_ID)
                .setServiceAccountPrivateKey(SecurityUtils.loadPrivateKeyFromKeyStore(SecurityUtils.getPkcs12KeyStore(), Main.class.getResourceAsStream(P12_FILE_NAME), STORE_PASS, ALIAS, KEY_PASS))
                .setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/plus.business.manage"))
                .setServiceAccountUser(IMPERSONATED_USER)
                .build();
				
mybusiness = new MyBusiness.Builder(httpTransport, jsonFactory, credential).setApplicationName(APPLICATION_NAME).build();

Any request after that leads to this Exception:

 

Exception in thread "main" com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized
401 Unauthorized
	at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
	at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
	at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
	at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:384)
	at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
	at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:217)
	at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:868)
	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)

Attempting to call credential.refreshToken() manually leads to this:

 

Exception in thread "main" com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized
	at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
	at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
	at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
	at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:384)
	at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)

As I said, there were no changes to the code. What should I do?

Re: Service Account - API randomly fails with "401 Unauthorized" error

Google Employee
# 5
Google Employee

Hi @Oleg G,

 

Since you are building a service account application, please make sure you are requesting user consent involving human interaction for each Google Account that you need to access the Google My Business data from and manually authorize your app during OAuth 2.0 flow at least once prior to authenticating with the public/private key pair. You can request user consent using OAuth 2.0 installed applications flow, OAuth 2.0 web server applications flow or via OAuth 2.0 Playground. However, you don’t have to save your retrieved refresh tokens for a service account application. Instead, you specify the user to impersonate by specifying the email address of the user account for access to their Google My Business data when you prepare to make authorized API calls. This is because a service account is an account that belongs to your application instead of an individual end user with a Google Account. Please check out this Accepted Solution for requesting user consent for a service account application.

 

I suggest you add this additional header to your requests to enable more detailed error messages in responses when using the Java client library:

X-GOOG-API-FORMAT-VERSION: 2

 

Thanks,

The Google My Business API team

Re: Service Account - API randomly fails with "401 Unauthorized" error

Visitor ✭ ✭ ✭
# 6
Visitor ✭ ✭ ✭

Hi @Shalini S,

 

Thank you for the response. As I already said, I have gone through the User Consent process numerous times for the account that I'm trying to impersonate. If I go to the settings of that account, I can see my App name in "Apps connected to your account" with the required scope: "View and manage your business listing on Google". This configuration used to work fine, but then suddenly broke (nothing changed in the code or settings) and the server only responds with 401 errors now.

 

I have removed all my credentials for the project and went through the process again. Here are my steps:

 

1. Create a Service Account, save .p12 file, service account ID.
2. Create an OAuth Client, save Client ID & secret, add "https://developers.google.com/oauthplayground" as a redirect URI.
3. Go to OauthPlayground, configure it to use my own Client ID and Client secret. Access type is set to Offline and prompt is set to Consent Screen.
4. Authorize "https://www.googleapis.com/auth/plus.business.manage" API, go through the consent process. Playground displays a consent screen which asks me to allow my app access to this account. I do that, go through Step 2 and 3 in the Playground, verify that I can send a simple request to "https://mybusiness.googleapis.com/v3/accounts" and receive a response. I also verify that my app has appeared in the "Apps connected to your account" list of the account I'm trying to impersonate.
5. In my Java app I use the .p12 file, service account ID and impersonated account's e-mail to send a simple request to the API - I receive an 401 Unauthorized error every time.

 

Adding the X-GOOG-API-FORMAT-VERSION header makes no difference in this case.

 

Please let me know what I'm doing wrong.

Service Account - API randomly fails with &quot;401 Unauthorized&quot; error

Visitor ✭ ✭ ✭
# 7
Visitor ✭ ✭ ✭

Update: just now the server has started responding with a different error:

 

400 Bad Request
{
"error" : "invalid_grant",
"error_description" : "Invalid JWT Signature."
}

 

This happened out of nowhere, I did not even recompile the project.

Re: Service Account - API randomly fails with &quot;401 Unauthorized&quot; error

Visitor ✭ ✭ ✭
# 8
Visitor ✭ ✭ ✭

I'm still receiving the 401 error, even though I've already been following all the steps you've listed.

 

Any solution?

Re: Service Account - API randomly fails with &quot;401 Unauthorized&quot; error

Google Employee
# 9
Google Employee

Hi @Oleg G,

 

The 401 UNAUTHORIZED error usually occurs due to invalid credentials. Please make sure you setup your service account properly. The following are the required steps you should take for the service account application to work with the Google My Business API:

 

1. You should make sure that you created a service account in your whitelisted project in Google API Console to access the API.

 

2. You should request user consent at least once for each Google Account you need to access the Google My Business data from. The end users will have to log in with their Google Accounts to manually authorize the application using OAuth 2.0 Authorization. They need to perform this process only once with an OAuth 2.0 client ID during the OAuth 2.0 installed application flow, OAuth 2.0 web server application flow (your other OAuth 2.0 client ID web app) or via OAuth 2.0 Playground. This process should be performed prior to using the service account application with its public/private key pair credentials and not while running it. You can verify or remove the authorized app from the Apps connected to your account page of each Google Account you requested user consent for.

 

3.  When you prepare to make authorized API calls using the service account to access a particular end user’s Google My Business data, you specify the user to impersonate by specifying the email address of the Google Account that you have previously given user consent for.

You should be able to get your service account application to work after you’ve applied the above steps. Please let me know if you still get the same issue again.

 

Thanks,

The Google My Business API team

Re: Service Account - API randomly fails with &quot;401 Unauthorized&quot; error

Visitor ✭ ✭ ✭
# 10
Visitor ✭ ✭ ✭

Hi @Shalini S,

 

I'm doing everything as you describe, but I'm still getting the issue. My problem is that I go through the user consent procedure, and verify that my Google account is connected to my Application in the Settings, but when I try to impersonate that user in Java using the Service Account I get the 401 error. If I don't try to impersonate anyone, the API responds to my requests, so I assume that means the service account itself is set up correctly...

 

The Service Account, and the OAuth2 client (that the consent is given to) belong to the same whitelisted project.

 

I'm confused, because the UI is telling me that the user has given the app access to their account, but the service account cannot actually impersonate that user. Since my code worked before, this must be a configuration issue, but I can't figure out what it is.

 

Are there any requirements for a Google account that needs to be later impersonated by a Service Account? Could you please describe the User Consent procedure for usage with a Service Account using the Oauthplayground service specifically? (I described my approach above, is it wrong?)