Getting a 403 - Forbidden for Google Service Account - google-api

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.

Related

Error using google API and OAUTH2 in Python to get user info

My web app is successfully going through google's recommended flow to get credentials to query Google Drive API. This works fine. However, when I try to use the same credentials already obtained to get the user's email and name, I get an error.
Here I retrieve credentials and query Google Drive API. This works perfectly fine
def analyze():
credentials = getCredentials()
drive_service = googleapiclient.discovery.build('drive', 'v3', credentials=credentials)
theFiles = drive_service.files().list(pageSize=1000,q="trashed=false", fields="files(id,name,modifiedTime, size)").execute() #THIS WORKS
Right after that, I try to use the SAME CREDENTIALS to get user info, but now it doesn't work
oauth2_client = googleapiclient.discovery.build('oauth2','v2',credentials=credentials)
user_info= oauth2_client.userinfo().get().execute() #THIS FAILS
givenName = user_info['given_name']
Error: https://www.googleapis.com/oauth2/v2/userinfo?alt=json returned "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.">
SOME OTHER IMPORTANT FUNCTIONS:
def getCredentials():
*Loads credentials from the session.*
sc = session['credentials']
credentials = google.oauth2.credentials.Credentials(token=sc.get('token'),
client_id=sc.get('client_id'),
refresh_token=sc.get('refresh_token'),
token_uri=sc.get('token_uri'),
client_secret=sc.get('client_secret'),
scopes=sc.get('scopes'))
the credentials are obtained in the callback page:
#app.route('/OAcallback')
def OAcallback():
flow =google_auth_oauthlib.flow.Flow.from_client_secrets_file('client_id.json', scopes=['https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/userinfo.profile'])
flow.redirect_uri = return_uri
authorization_response = request.url
flow.fetch_token(authorization_response=authorization_response)
credentials = flow.credentials
* Store the credentials in the session.*
credentials_to_dict(credentials)
Please help me understand why my credentials are not working when trying to get user info. What should I change?
Thanks in advance!!!
You are only requesting the profile scope. To also request the email address add the scope email.
Change this part of your code from:
scopes=['https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/userinfo.profile']
to:
scopes=['https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/userinfo.profile' 'email']

Google API - Account disabled and then re-enabled, now "Not Authorized to access this resource/api"

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'.

Google Domain Shared Contacts API Using OAuth 2.0 for Server to Server Applications - Credential params

I'm trying to get the "Google Domain Shared Contacts API" described here:
https://developers.google.com/admin-sdk/domain-shared-contacts/
Working using "OAuth 2.0 for Server to Server Applications" described here:
https://developers.google.com/accounts/docs/OAuth2ServiceAccount
The recommendation from the OAuth page is to use the provided Google client library... I'm using the Java library. But the Shared-Contacts API doesn't have an example that uses this library, and I'm having trouble figuring out how to use the library with the Shared-Contacts API.
I am able to make the example for the OAuth to work for me... It uses Google Cloud Storage. Here's a snippet of the code:
String STORAGE_SCOPE = "https://www.googleapis.com/auth/devstorage.read_write";
try {
try {
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
String p12Content = Files.readFirstLine(new File(keyFile), Charset.defaultCharset());
// Build service account credential.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(Collections.singleton(STORAGE_SCOPE))
.setServiceAccountPrivateKeyFromP12File(new File(keyFile))
.build();
// Set up and execute Google Cloud Storage request.
String URI;
URI = "https://storage.googleapis.com/" + BUCKET_NAME;
HttpRequestFactory requestFactory = httpTransport.createRequestFactory(credential);
GenericUrl url = new GenericUrl(URI);
HttpRequest request = requestFactory.buildGetRequest(url);
HttpResponse response = request.execute();
content = response.parseAsString();
} catch (IOException e) {
System.err.println(e.getMessage());
}
} catch (Throwable t) {
t.printStackTrace();
}
return content;
It's a request to get a listing of what's in a certain bucket on GCS. It calls a specific URL using the Credentials object, where the Credentials object does the work of the OAuth, using a key file I downloaded. There's other steps involved for getting it to work (setting the service account email, etc), which I did. It returns an xml string containing what is inside the bucket, and it works fine for me.
I then tried changing the URI to this string:
URI = "https://www.google.com/m8/feeds/contacts/myGoogleAppsDomain.com/full";
and I changed the STORAGE_SCOPE variable to be this string:
STORAGE_SCOPE = "http://www.google.com/m8/feeds/";
Hoping it would then return an xml-string of the shared-contacts. But instead, it returns this error:
403 Cannot request contacts belonging to another domain
I believe I'm getting this error because I'm not specifying the "hd" parameter when I do the authentication request... However, I'm unsure how I can specify the "hd" parameter using the GoogleCredential object (or the other parameters, except for "scope")... Can someone help me with that?
I think the issue here is that you are not specifying which user you want to impersonate in the domain (and you haven't configured the security settings in your domain to authorize the service account to impersonate users in the domain).
The doubleclick API auth documentation has good examples on how to do this. You can use their sample and replace the scopes and API endpoint:
https://developers.google.com/doubleclick-publishers/docs/service_accounts#benefits

Possible to use Google service account with Sites APIs

I am using a service account to impersonate an admin user successfully with the Admin Directory and Google+ Domain APIs, but I am unable to figure out if Sites can make use of service accounts. Is it even possible? I am referring to the API that allows you to create and delete sites, not the Webmaster Tools API for managing the content and so on.
You can use Service Accounts with Sites API. All you need to do is use SignedJwtAssertionCredentials, impersonate the user from background and access the GData APIs.
Here is the code snippet:
credentials = SignedJwtAssertionCredentials(
SERVICE_ACCOUNT_EMAIL,
file(SERVICE_ACCOUNT_PEM_FILE_PATH, "rb").read(),
scope=["https://sites.google.com/feeds/"],
prn=userEmailYouWantToImpersonate
)
http = httplib2.Http()
http = credentials.authorize(http)
sitesClient = gdata.sites.client.SitesClient(source='mycompany', site='mySite', domain='mydomain')
sitesClient.auth_token = TokenFromOAuth2Creds(credentials)
feed = sitesClient.GetContentFeed()
class TokenFromOAuth2Creds:
def __init__(self, creds):
self.creds = creds
def modify_request(self, req):
if self.creds.access_token_expired or not self.creds.access_token:
self.creds.refresh(httplib2.Http())
self.creds.apply(req.headers)
Yes it is possible. Here's a code example :
SitesService sitesService = new SitesService("MyApplication");
final GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(new NetHttpTransport()).setJsonFactory(new JacksonFactory())
.setServiceAccountId("XXXX.apps.googleusercontent.com")
.setServiceAccountScopes(Collections.singleton(SERVICE_SCOPES))
.setServiceAccountPrivateKeyFromP12File(new File("key.p12"))
.setServiceAccountUser("mysiteowner#domain.com").build();
sitesService.setOAuth2Credentials(credential);
The only thing you have to be cautious with, is that some SitesService methods might not be able to refresh the token properly, in which case you will have to catch the SessionExpiredException and refresh the Credential yourself.
Google Sites API pages says [1] that you can "Create new Sites or copy existing Sites" with it.
[1] https://developers.google.com/google-apps/sites

Invalid grant when accessing Google API

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

Resources