Getting authorization token-key for my google service account - google-api

I actually want to reply my google play app's comments using this api:
https://developers.google.com/android-publisher/reply-to-reviews#gaining_access
But in here, it wants me to enter auth_token. First i added service account to my google play. After that, i created a key and i downloaded the json file for my key. Using this json file i tried to get auth. token but output of this code tells me that credentials are invalid and token is "none". I wanna solve this problem. Thanks.
from google.oauth2 import service_account
import googleapiclient.discovery
SCOPES = ['https://www.googleapis.com/auth/cloud-platform',
'https://oauth2.googleapis.com/token',
'https://accounts.google.com/o/oauth2/auth',
'https://www.googleapis.com/oauth2/v1/certs',
'https://www.googleapis.com/robot/v1/metadata/x509/myusername.gserviceaccount.com']
SERVICE_ACCOUNT_FILE = 'service.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
print(credentials._token_uri)
print(dir(credentials))
print(credentials.valid)
print(credentials.token)
Resources that i researched:
https://developers.google.com/identity/protocols/oauth2#serviceaccount
https://developers.google.com/identity/protocols/oauth2/service-account#python_1
https://developers.google.com/android-publisher/reply-to-reviews#gaining_access
Note: Also people are sending their API requests like this:
service = build('drive', 'v3', credentials=credentials)
For example above code is for sending api request for google drive. How can i api for my purpose? For example i wanna access google play api using "build".

The key to the build method is that first it takes the API then it takes the version of the api you want to access followed by your credentials.
service = build('drive', 'v3', credentials=credentials)
If you check Discovery services you will find a list of all google apis
The entry for android publisher is as follows
{
"kind": "discovery#directoryItem",
"id": "androidpublisher:v3",
"name": "androidpublisher",
"version": "v3",
"title": "Google Play Android Developer API",
"description": "Lets Android application developers access their Google Play accounts.",
"discoveryRestUrl": "https://androidpublisher.googleapis.com/$discovery/rest?version=v3",
"icons": {
"x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png",
"x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png"
},
"documentationLink": "https://developers.google.com/android-publisher",
"preferred": true
},
Which means that the following should be what you are looking for.
service = build('androidpublisher', 'v3', credentials=credentials)

Related

How to use the on-behalf-of flow to call Microsoft Graph from Spring resource server

I'm trying to complete a fairly simple process:
A web application, authenticated with Azure AD via a personal Microsoft account (i.e. an #hotmail account), makes a call to a second microservice.
The microservice is secured via a JWT, obtains a second token using the on-behlaf-of flow, and called the Microsoft Graph API to retrieve calendar events.
I can log into the web frontend OK, and call the second microservice OK. The second microservice can obtain an on-behalf-of (obo) token OK, but the problem I run into is that the obo access token provided to me fails to call the Graph API. The error I receive is this:
{"error":{"code":"NoPermissionsInAccessToken","message":"The token contains no permissions, or permissions can not be
understood.","innerError":{"oAuthEventOperationId":"7499efa7-932b-425d-8ad4-43206630f961","oAuthEventcV":"ed1BzGz2t/H/wK7JnZB6lQ.1.1.1","errorUrl":"https://aka.ms/autherrors#error-InvalidGrant","requestId":"3ca5fa79-423b-460e-9130-b7d1172ec841","date":"2021-09-13T09:24:17"}}}
My problem is similar to the one described here, where my obo JWT does not contains a roles claim. This is the decoded JWT sent to the Graph API:
{
"typ": "JWT",
"nonce": "A3IzBCOGrnE53ukPqb2jHjWYT0grwFbHo_OkzcUhSRc",
"alg": "RS256",
"x5t": "l3sQ-50cCH4xBVZLHTGwnSR7680",
"kid": "l3sQ-50cCH4xBVZLHTGwnSR7680"
}.{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/26375159-666a-4217-adfe-c06427b7798c/",
"iat": 1631522998,
"nbf": 1631522998,
"exp": 1631526898,
"acct": 1,
"acr": "1",
"aio": "AUQAu/8TAAAA1HEurZSE5YS0ADYs3oKeEEy6qhWwyXBZtoHtBPbIS/jo0OD5BTlQptuXZ3ZLtDczWZQw7b0+dUoCNdpN2mY4ew==",
"altsecid": "1:live.com:00014B90C0D373BC",
"amr": [
"pwd"
],
"app_displayname": "SpringBootMicroserviceDemo",
"appid": "5907efc0-f0b5-45db-b4cf-725f655009c3",
"appidacr": "1",
"email": "matthewcasperson#hotmail.com",
"family_name": "Casperson",
"given_name": "Matthew",
"idp": "live.com",
"idtyp": "user",
"ipaddr": "45.132.224.55",
"name": "matthewcasperson",
"oid": "e377a23b-1b88-4d14-9c99-fc6ecd4a41c7",
"platf": "3",
"puid": "1003200180F4FB20",
"rh": "0.AVAAWVE3JmpmF0Kt_sBkJ7d5jMDvB1m18NtFtM9yX2VQCcNQAKM.",
"scp": "Calendars.Read openid profile User.Read email",
"signin_state": [
"kmsi"
],
"sub": "vhbBIoJqEHoEaJLMVsG5sh0C4FjoJiAfAKOFCzrC8hQ",
"tenant_region_scope": "NA",
"tid": "26375159-666a-4217-adfe-c06427b7798c",
"unique_name": "live.com#matthewcasperson#hotmail.com",
"uti": "ufe_nSFv_UuiYCOur72mAQ",
"ver": "1.0",
"wids": [
"13bd1c72-6f4a-4dcf-985f-18d3b80f208a"
],
"xms_st": {
"sub": "PCUskXV6aCsNgHz9Yug42-WhS-iea1gy5GI5trkTZ4E"
},
"xms_tcdt": 1631479794
}.[Signature]
The JWT has Calendars.Read in the scp, but no roles, which appears to be an issue.
The JWT above will complete a call to the "me" endpoint OK (i.e. https://graph.microsoft.com/v1.0/me). Unfortunately, calling the "me" endpoint is as far as any example provided by Microsoft goes.
The sample application here provides a controller that shows calling the "me" endpoint on the Graph API, and if I modify the Graph API client to include the https://graph.microsoft.com/Calendars.Read scope, it also fails the Graph API request.
The sample application here appears to be a slightly older example in that it builds the token manually rather than injecting something like #RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient client (which is described in more detail on the Spring blog here). But again the sample only goes so far as to call the "me" endpoint.
As far as I'm aware I have enabled all the correct settings. My Azure AD Application has requested Calendars.Read both as a delegated and application permission, and the permissions have been granted consent:
My web based application can be found here, and the Calendar API microservice can be found here.
Further reading:
Azure AD v2 roles not included in Access Token: This described how to add custom roles to the JWT, but I did not see how it could be used to add Graph API roles.
Graph API access token for calendar read reports No Permissions: This talked about adding application permissions, which did not resolve the issue for me.
Microsoft Graph API outlook task folder : NoPermissionsInAccessToken: This issue talks about different OAuth flows, but this doesn't appear to apply to a resource server that accepts a JWT.
How to solve "NoPermissionsInAccessToken" returned by Microsoft Graph when reading calendar or sending mail: The answer here talks about "Adding guest users is meaningless", which makes no sense to me.
Spring Boot Starter for Azure Active Directory developer's guide: This is a reasonably detailed tutorial that even speaks about resource servers, but I could not find any tips that addressed the NoPermissionsInAccessToken error.
OAuth 2.0 Sample for Azure AD Spring Boot Starter client library for Java - This is another reasonably detailed tutorial that talks about accessing a second resource server, but again I didn't find any clues to the NoPermissionsInAccessToken error.
List events and Get calendar: This indicates that a Delegated (personal Microsoft account) can use the Calendars.Read permission.
The crux of my issue is that using the on-behalf-of flow appears to be the recommended solution for calling the Graph API from a resource server that accepts a JWT from a front end application. Defining the delegated permissions in the Azure AD Application, consenting to them, defining them in an authorization-clients: in the application.yaml file, and getting a client via #RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient client are the only consistent instructions from the Microsoft documentation and sample applications. And yet the resulting JWT can only call the "me" endpoint.
Can anyone shed some light on how to call the Graph API with on-behalf-of token in Spring boot?

Try this API for Logging API returns "PERMISSION_DENIED"

I am trying to test the Google Cloud Logging API on the "Try this API" feature that Google Cloud Platform has on their documentation, but I get this response back:
{
"error": {
"code": 403,
"message": "The caller does not have permission",
"status": "PERMISSION_DENIED"
}
}
I know that my response body is correct because it works with OAuth 2.0 but fails when I use API Key.
Auth 2.0:
Working request using OAuth 2.0
API Key:
Non-Working request using API Key
Google docs says that they generate their own API Key for this "Try this API" feature. https://developers.google.com/explorer-help/
Since Google is using their own API Key, I do not understand why I am getting a response status of PERMISSION_DENIED.
Edit:
Here is a link to the Try this API feature in Google Cloud Platform if you would like to give it a try. https://cloud.google.com/logging/docs/reference/v2/rest/v2/entries/write?apix_params=%7B%22resource%22%3A%7B%22entries%22%3A%5B%7B%22logName%22%3A%22projects%2F%5BPROJECT_ID%5D%2Flogs%2Frequests%22%2C%22resource%22%3A%7B%22type%22%3A%22http_load_balancer%22%2C%22labels%22%3A%7B%7D%7D%7D%5D%7D%7D
Here is the python request that I am using in my code to create an entry:
import requests
entry = {
"entries": [
{
"logName": "projects/[PROJECT_ID]/logs/requests",
"resource": {
"type": "http_load_balancer",
"labels": {}
}
}
]
}
requests.post('https://logging.googleapis.com/v2/entries:write?key=[YOUR_API_KEY]', data=json.dumps(entry))
The API key was created from my user that has "logs writer", "logs viewer", and "logging admin" permissions. This theoretically should be all the permissions I need to make the post request. However, it is still returning a "PERMISSION_DENIED" status.
Any help would be much appreciated. Thank you in advance.
It looks like you are making a request to write data which isn't publicly writable. API Keys have no concept of user, they are only identifying you are allowed to call an API. So it looks like your API key request is working to the extent it can, but the response is telling you: I don't know who you are so I can't let you do this.
OAuth 2.0 is the solution here, as it acts on behalf of your account (you have to give consent), allowing the API to verify you have permission to take this action.
Service accounts are another option, to act on behalf of your project instead of your user, but they aren't practical from a web UI.

Using Azure Active Directory token in ASP.Net Core Web API with UseJwtBearerAuthenticiation

I have a native client application written in Ionic Framework 3. I have a Web API written in ASP.NET Core 1.1. I want to use Azure Active Directory to manage access to the Web API.
I have registered two applications with Azure Active Directory: Mobile App and Web API. The Mobile App has the required permission of granting access to the Web API. Below are screen shots of the permissions from our Azure Admin Portal:
Mobile App Permissions
This is configured as a Native App in AAD. I have an Application ID and an Object ID given by AAD. Additionally, I added an arbitrary Redirect URI, which I thought based on several tutorials did not need to resolve, that URI is http://mobileCRMApp. Looking at the Properties in AAD, the Home page URL is blank and the Logout URL is blank.
API Permissions
BOLD UPDATED 10/03/2017:
This is configured as a Web App/API in AAD. I have an Application ID and an Object ID given by AAD. Additionally, I set both the Home Page URL and the App ID URI to match the root of my Web API (https://crm.mycompany.com).
My Ionic client application successfully authenticates against AAD roughly in the following way:
authenticate(userID, authCompletedCallback) : any {
let parent = this;
//this.context = new AuthenticationContext(this.config.authority);
let context = this.msAdal.createAuthenticationContext("https://login.microsoftonline.com/myTenantId");
console.log(context);
context.acquireTokenAsync(parent.config.resourceUri, parent.config.clientId, parent.config.redirectUri, userID, "")
.then(authCompletedCallback)
.catch((e: any) => console.log('Authentication failed', e));
}
The login process goes fine in the app, and the callback receives a token, and I can translate its payload using jwt.io into roughly the following:
{
"aud": "https://crm.mycompany.com/",
"iss": "https://sts.windows.net/someID/",
"iat": 1506539211,
"nbf": 1506539211,
"exp": 1506543111,
"acr": "1",
"aio": "someOtherID",
"amr": [
"pwd"
],
"appid": "appID",
"appidacr": "0",
"e_exp": 262800,
"family_name": "Walter",
"given_name": "Philip",
"ipaddr": "someAddress",
"name": "Philip Walter",
"oid": "someOtherID",
"onprem_sid": "someOtherID",
"puid": "stuff",
"scp": "user_impersonation",
"sub": "e_X7WlAoVS2vzXm1pr3kcDOrET7czcC0f8-YRU_2DJ8",
"tenant_region_scope": "NA",
"tid": "ourTenantID",
"unique_name": "pwalter#advtis.com",
"upn": "pwalter#advtis.com",
"uti": "RLvLlibQHESwmujVBBdlAA",
"ver": "1.0"
}
So then I take the token and send it along with an http request to the API from the Ionic client app, roughly like so:
let headers = new Headers();
headers.append('Authorization', 'Bearer ' + this.authToken.accessToken);
let options = new RequestOptions({ headers : headers });
this.data.http.get(url, options).map(res => res.json()).subscribe((data) => {
console.log(data);
});
The API then has the following in Startup.cs
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = Configuration["Authentication: AzureAd:AADInstance"] + Configuration["Authentication: AzureAd:TenantId"],
Audience = Configuration["Authentication: AzureAd:Audience"]
});
So, I can log in through AAD in the client app, and I receive a token, but I still get a 401 unauthorized response from the web api when I send a request to a route with the [Authorize] tag above it.
I am obviously doing something wrong in configuring the API or the permissions. I put this together using several different tutorials, because I could not find anything that specifically addressed my use case. Any ideas as to what I'm doing wrong, or how I might troubleshoot?
Why does your audience say "aud": "https://graph.windows.net"?
It should say "aud": "https://yourWebApi.azurewebsites.net" or something similar.
The token you included above is only good for the Graph API, any other Azure AD protected resource will refuse it, including your Web API, since it expects the audience to match self.
parent.config.resourceUri is probably where you source the desired audience from:
context.acquireTokenAsync(
parent.config.resourceUri,
...
According to RFC 7519:
The "aud" (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the "aud" claim when this claim is present, then the JWT MUST be rejected. In the general case, the "aud" value is an array of case- sensitive strings, each containing a StringOrURI value. In the special case when the JWT has one audience, the "aud" value MAY be a single case-sensitive string containing a StringOrURI value. The interpretation of audience values is generally application specific. Use of this claim is OPTIONAL.
Sometimes it's called resource, sometimes audience, sometimes aud... it is what it is :)

Gmail API with service account

Under mygmailaccount#gmail.com in the API console I created a project and I am successfully using a service account for the Analytics API and Search Console API services. I've now enabled the Gmail V1 API and ran this code:
gmail_scopes = [
"https://mail.google.com/",
"https://www.googleapis.com/auth/gmail.compose",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/gmail.readonly"
]
gmail = Google::Apis::GmailV1::GmailService.new
gmail.authorization = Google::Auth.get_application_default(gmail_scopes)
This is the same code (with different scopes of course) that I use to authorize the Analytics and Search Console API.
However I get BAD REQUEST Failed Precondition errors when I run this:
gmail.get_user_profile("mygmailaccount#gmail.com")
Some googling tells me that is because the API is trying to access the non-existent inbox for the serviceaccount email address and that in the initiatlization process I need to include mygmailaccount#gmail.com so that the API service will use that inbox.
How do I do that?
I've tried the following '...' always being the email address
gmail.authorization = Google::Auth.get_application_default(gmail_scopes, username: '...')
gmail.authorization = Google::Auth.get_application_default(gmail_scopes, {username: '...'})
gmail.authorization.username = '...'
Service accounts cannot access #gmail.com mailboxes. You need to use an OAuth2 UserAuthorizer. See the Gmail API Ruby Quickstart guide for an example.

Google Credential: This developer account does not own the application

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

Resources