Huawei AppGallery Connect API - 403 client token authorization fail - android

I am trying to automate the app publishing process to the Huawei store using the REST APIs as mentioned in the link.
https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agcapi-overview
I successfully received an access token but other operations(ex: getting the app info, getting the upload URL) are getting failed with the below status code and error.
403 client token authorization fail.
I did not write any code, I simply used the below sample code and updated clientId, clientSecret, appId.
https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Examples/agcapi-publish_api_code
What could go wrong?

Update:
Set Project to N/A to define the API client as a team-level one.
Set Roles to Administrator
Please check whether your client role is Administrator.
The role of a member determines the permissions in AppGallery Connect. Administrator has most operation permissions, being able to add member accounts and assigning permissions to them. For details about the mapping between roles and permissions, please refer to Roles and Permissions.
Work with AppGallery Connect API
To call the AppGallery Connect API, you need to obtain authorization from the AppGallery Connect server in advance using either of the following modes: API client mode and OAuth client mode. To call the AppGallery Connect API in the API client mode, you need to manage your API client in AppGallery Connect. An API client can be managed only by your team account holder. The basic process is as follows:
A. Creating an API Client
Sign in to AppGallery Connect and select Users and permissions.
Go to Api Key > AppGalleryConnect API from the navigation tree on the left and click Create.
Set Name to a customized client name, set Roles to the corresponding role, and click Confirm.
After the client is successfully created, record the values of Client ID and Key in the client information list.
Check the screenshot below:
B. Obtaining the Token for Accessing the API
After an API client is created, the API client needs to be authenticated in AppGallery Connect. After the authentication is successful, the API client obtains an access token for accessing the AppGallery Connect API. With this access token, you can access the AppGallery Connect API.
To obtain an access token, you need to add the code for calling the Obtaining a Token API to your app program.
public static String getToken(String domain, String clientId, String clientSecret) {
String token = null;
try {
HttpPost post = new HttpPost(domain + "/oauth2/v1/token");
JSONObject keyString = new JSONObject();
keyString.put("client_id", "18893***83957248");
keyString.put("client_secret", "B15B497B44E080EBE2C4DE4E74930***52409516B2A1A5C8F0FCD2C579A8EB14");
keyString.put("grant_type", "client_credentials");
StringEntity entity = new StringEntity(keyString.toString(), Charset.forName("UTF-8"));
entity.setContentEncoding("UTF-8");
entity.setContentType("application/json");
post.setEntity(entity);
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpResponse response = httpClient.execute(post);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
BufferedReader br =
new BufferedReader(new InputStreamReader(response.getEntity().getContent(), Consts.UTF_8));
String result = br.readLine();
JSONObject object = JSON.parseObject(result);
token = object.getString("access_token");
}
post.releaseConnection();
httpClient.close();
} catch (Exception e) {
}
return token;
}
After obtaining an access token, you can use the access token for identity authentication when accessing the AppGallery Connect API. The default validity period of an access token is 48 hours. If an access token expires, you need to obtain a new access token.
C. Accessing the API
After obtaining an access token, you can use the access token to call the AppGallery Connect API to complete function development.

As said in this comment, Once I set the project to NA it started working.
Thanks to #shirley

Related

Getting a Google Speech API access token for a production Android app

I'm using the Google Speech API in an Android app. The README states:
In this sample, we load the credential from a JSON file stored in a raw resource folder of this client app. You should never do this in your app. Instead, store the file in your server and obtain an access token from there.
Are there any samples regarding how to properly obtain an access token for a production app?
From what I've gathered, it seems that I can use Application Default Credentials provided via Compute Engine or GAE, but I have no idea how to actually respond with an access token to my app.
I'm the author of the sample.
Here's the part that should be on the server side. Basically, you have to store the JSON file securely on your server side, and fetch a new access token when you get a request from your mobile app. Mobile app should always talk with your server to get an access token. This way, you don't need to put your service account key in the APK.
// In response to a request from a mobile client
// Open the JSON file stored on the server side.
final InputStream stream = ...;
try {
// Initialize the credentials
final GoogleCredentials credentials =
GoogleCredentials.fromStream(stream)
.createScoped(SCOPE);
// Fetch a new access token.
final AccessToken token = credentials.refreshAccessToken();
// Return the token to the mobile app.
} catch (IOException e) {
// Maybe report this as an HTTP error.
}
If you don't use Java on your server side, you can find a client library for the language you use at Google Cloud Client Libraries.
Refer to Google Cloud Platform Auth Guide for basics about how to send requests from your backend to the API.
For Android, you might instead want to use the Android sample for Google Cloud Speech as the Cloud client library does not currently focus on Android support.

GCM Client Based Device Group Management

I'm having issues adding GCM registration ID's to a device group client side in my Android app. I've followed all the instructions from https://developers.google.com/cloud-messaging/android/client-device-group but keep getting a 401 HTTP response. I've found the following posts but no one has an answer...
get notification key error 401 gcm https://android.googleapis.com/gcm/googlenotification
Google Cloud Messaging, returning 401 Unauthorized
Google Cloud Messaging, 401 Unauthorized is returned when creating notification key from client
How to successfully "Generate a Notification Key on the Client" with GCM?
I'm successfully getting an auth token from GoogleSignInApi and the method provided in Google's instructions but both give back 401 responses. I've ensured that I'm using the client ID for a Web Application in my Google Developer Console and still no luck. Here is my code snippet...
URL url = new URL("https://android.googleapis.com/gcm/googlenotification");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true);
// HTTP request header
con.setRequestProperty("project_id", getString(R.string.gcm_defaultSenderId));
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Accept", "application/json");
con.setRequestMethod("POST");
con.connect();
String accountName = getAccount();
//Initialize the scope using the client ID you got from the Console.
final String scope = "audience:server:client_id:"
+ "MY_WEB_APP_CLIENT_ID";
String idToken = "";
try {
idToken = GoogleAuthUtil.getToken(this, sharedPref.getString("googleEmail", ""), scope);
} catch (Exception e) {
e.printStackTrace();
}
// HTTP request
JSONObject data = new JSONObject();
data.put("operation", "add");
data.put("notification_key_name", "my_group_name");
data.put("registration_ids", new JSONArray(Arrays.asList(registrationId)));
data.put("id_token", idToken);
OutputStream os = con.getOutputStream();
os.write(data.toString().getBytes("UTF-8"));
os.close();
int responseCode = con.getResponseCode();
I don't think it matters for this HTTP post request but I've also ensured the right project and client ID (android) are stored in my google-services.json. Has anyone had any success managing device groups client side? If so what's different in my code from yours?
I'm not sure that it is possible to do this without the server API key. The 401 error indicates that some sort of HTTP Authorization header should be included if that URL is used for device group setup.
My best suggestion is to keep the server API key well hidden using a client-side keystore mechanism (http://developer.android.com/training/articles/keystore.html).
For details on how do do the whole thing using the SERVER_API key, please see here: Google Cloud Messaging (GCM) with local device groups on Android gives HTTP Error code 401
Hope this helps. :-)

Android app with Google+ API: Client ID or Android ID?

I'm trying to develop an application that makes use of some basic Google+ Apis. The app request a token from Google that then upload on my server and my server check for its validation with Google. What I don't understand (I'm a bit confused) is what's the difference between Client ID for Web application and Client ID for Android application in Google Developers Console. I've tried both of them on the Android app and both work (successfully obtained a token). Obviously, when using the Web Client ID, my SCOPE that I pass using GoogleAuthUtil.getToken() is different from the one using Android Client ID. So, what's the difference between them? I think that I should go for the Android Client ID, but I'd like to know the really differences.
On client side I use:
final String token = GoogleAuthUtil.getToken(mContext, userEmail, G_PLUS_SCOPE);
Where G_PLUS_SCOPE = oauth2:https://www.googleapis.com/auth/plus.me
On server side, I check with Google with this code:
GoogleIdToken token = GoogleIdToken.parse(mJFactory, getRequest().getAuthToken());
if (mVerifier.verify(token)) {
GoogleIdToken.Payload tempPayload = token.getPayload();
if (!tempPayload.getAudience().equals(mAudience)) {
problem = "Audience mismatch";
errorMessage = new ErrorMessage(ErrorCodes.AUDIENCE_MISMATCH,problem,null);
mResponse = new ErrorResponse( errorMessage);
}
else if (!mClientIDs.contains(tempPayload.getAuthorizedParty())) {
problem = "Client ID mismatch";
errorMessage = new ErrorMessage(ErrorCodes.CLIENT_ID_MISMATCH,problem,null);
mResponse = new ErrorResponse(errorMessage);
}
I also don't understand what's the exact value of mAudience. Do I need to put the Client ID as mAudience? And, is the mClientIDs the array containing all the key (Including the Android client ID key)?
Thanks for your help
EDIT: Following http://android-developers.blogspot.it/2013/01/verifying-back-end-calls-from-android.html I've read that the Audience is the Client ID for Web Application and the mIds are all the ID for installed application (1 for me because I've only Android). But I'm not sure if this is the right way of thinking it for every case.
I don't have answer to your question but I found this blog which can help you out:
http://www.androidhive.info/2014/02/android-login-with-google-plus-account-1/
I hope this helps you.
Technically the audience is the client ID which the ID token is intended to authenticate the user to, where the authorized party is the client ID which the ID token was issued to:
http://openid.net/specs/openid-connect-core-1_0.html#IDToken
You should verify both when both are provided. When requesting an ID Token from your Android app, you should use the Client ID of your server to make the request. Your server should then verify that it is the audience for the token when it receives it from your Android client.

Getting Google one-time authorization code

I am having trouble getting a one-time authorization code from Google. I am attempting to get the authorization code from an Android client so that I can send it to my Rails backend (web client).
In my Google Cloud Developer Console I have an application with two Client IDs:
Client ID for web application (for my rails backend)
Client ID for Android application (for my android client). The SHA1 used is from ~/.android/debug.keystore
Suppose the Web Application Client ID is 12345.apps.googleusercontent.com
Suppose the Android Client ID is 67890.apps.googleusercontent.com
This is some of my code:
private final static String WEB_CLIENT_ID = "12345.apps.googleusercontent.com";
private final static String GOOGLE_CALENDAR_API_SCOPE = "audience:server:client_id:" + WEB_CLIENT_ID;
private void getAndUseAuthToken(final String email) {
AsyncTask task = new AsyncTask<String, Void, String>() {
#Override
protected String doInBackground(String... emails) {
try {
return GoogleAuthUtil.getToken(AddExternalCalendarsActivity.this, emails[0], GOOGLE_CALENDAR_API_SCOPE);
} catch (UserRecoverableAuthException e) {
startActivityForResult(e.getIntent(), IntentConstants.REQUEST_GOOGLE_AUTHORIZATION);
} catch (IOException e) {
e.printStackTrace();
} catch (GoogleAuthException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String authToken) {
if (authToken != null) {
saveTokenAndGetCalendars(email, authToken);
}
}
};
String[] emails = new String[1];
emails[0] = email;
task.execute(emails);
}
Some additional notes
I am hitting the GoogleAuthException and receiving "Unknown" as the message detail
I'm unable to add additional members in the permissions of the Google Cloud Console for this project - when adding a new member a popup appears with "Server Error. Whoops! Our bad.". I have sent feedback to Google twice.
I'm referring to this documentation. Notice the quote below. By "fixed", are they saying that I do not need to prepend audience:server:client_id in my GOOGLE_CALENDAR_API_SCOPE variable? I've tried both with and without and still getting the same GoogleAuthException.
In this situation, the Android app can call the
GoogleAuthUtil.getToken() method on behalf of any of the Google
accounts on the device, and with a scope argument value of
audience:server:client_id:9414861317621.apps.googleusercontent.com.
The prefix audience:server:client_id: is fixed, and the rest of the
scope string is the client ID of the web component.
If I use this scope, I can authenticate with google from device. However, the documentation I've read suggests that I need to use the server web client id (which is in the same google console project as the android client id) in the scope in order for the server to hit the google api on behalf of the user who authorized it on the android client:
private final static String GOOGLE_CALENDAR_API_SCOPE = "oauth2:https://www.googleapis.com/auth/calendar";
UPDATE 1
I originally added in answer:
The reason for my first problem - I am hitting the GoogleAuthException and receiving "Unknown" as the message detail - was a mistake I made when adding the android client id in the cloud console. The SHA1 was correct but I did not type the package name correctly. I used com.company.app when my android package is actually com.company.android.app.
The code in the original question works fine. Just make sure you have all the necessary clients in your Google Cloud Console project.
But another problem still exists. When I send the one-time authorization token returned from GoogleAuthUtil.getToken() to the Rails backend, and then try to exchange it for an access_token and refresh_token, I get the follow:
Signet::AuthorizationError:
Authorization failed. Server message:
{
"error" : "invalid_grant"
}
This google documentation and several SO posts suggests that I need to set access_type=offline. But I think that is when you are requesting the one-time authorization code and offline access from a Web Server. I'm trying to request the one-time authorization code from an Android client and send it to the web server.
Is this possible with GoogleAuthUtil.getToken()?
UPDATE 2
Google Plus login must be in the scope even if you're only trying to access the calendar:
private final static String GOOGLE_CALENDAR_API_SCOPE = "oauth2:server:client_id:" + WEB_CLIENT_ID + ":api_scope:https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/calendar";
This SO post was helpful. Also, Google's Cross Client identity documentation does state:
[Note: This policy in being rolled out gradually. For the moment, when access tokens are involved, it only applies when the requested scopes include https://www.googleapis.com/auth/plus.login.]
I'll summarize in an answer if the token exchange works on Rails backend.
Two things solved this for me:
Make sure the Android and Web Client IDs are setup in correctly in the same Google Cloud Console project.
Use the correct scope. Plus login is required even if you're only accessing the calendar api:
// the id of the web server that is exchanging the auth code for access and refresh tokens
private final static String WEB_CLIENT_ID = "12345.apps.googleusercontent.com";
private final static String GOOGLE_CALENDAR_API_SCOPE = "oauth2:server:client_id:" + WEB_CLIENT_ID + ":api_scope:https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/calendar";
Here's what I've done:
final String authToken = GoogleAuthUtil.getTokenWithNotification (this.context, account.name, "oauth2:" + AuthUtils.profileScope, new Bundle (),
Contract.authority, new Bundle ());
But you plain getToken should work the same for a foreground activity.
Take this token, send it to the server in an a way you can use it like an HTTP header (over HTTPS). As long as the server scope is a subset of the scope used to acquire the token, you shouldn't have a problem. The server uses the server id and the android client uses the android client id.
You should set your scope to either:
oauth2:https://www.googleapis.com/auth/calendar
or
oauth2:https://www.googleapis.com/auth/calendar.readonly
See https://developers.google.com/google-apps/calendar/auth
Edit: OK I see what you are trying to do. The scope value is a space separated list so you would likely need to append audience:server:client_id: to your scope like:
"oauth2:https://www.googleapis.com/auth/calendar audience:server:client_id:12345-your-web-component-client-id"

How to call https://www.googleapis.com/plus/v1/people/me at google

I am developing an Android application and need to get the "me" info from google but I always ends up in either response code 401 or 403. What am I doing wrong?
Here is my code:
private static final String GOOGLE_AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/plus.me";
I get the oauth token by (note...code below is shortened):
Account googleAccount = (AccountManager) getSystemService(ACCOUNT_SERVICE).getAccountsByType("com.google")[0];
final Bundle bundle = manager.getAuthToken(googleAccount, GOOGLE_AUTH_TOKEN_TYPE, true, null, null).getResult();
String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
So far so good... I now have a token so everything looks good here.
Now get the me info:
String GOOGLE_ME_URL = "https://www.googleapis.com/plus/v1/people/me";
final DefaultHttpClient client = new DefaultHttpClient();
final HttpGet request = new HttpGet(GOOGLE_ME_URL);
request.addHeader("Authorization", "OAuth=" + authToken);
final HttpResponse response = client.execute(request);
This gives response code 401.
I have also tried:
final DefaultHttpClient client = new DefaultHttpClient();
final HttpGet request = new HttpGet(GOOGLE_ME_URL + "?access_token=" + authToken);
final HttpResponse response = client.execute(request);
This gives response code 403 - Something like "Daily limit exceeded. Please sign up".
What am I doing wrong? what have I missed? How should this be done?
Thanks
// Edits below
Some more investigation:
I added a project into code.google.com/apis/console and took the key generated from there and put into the url, like:
https://www.googleapis.com/plus/v1/people/me?key=my_generated_key&access_token=" + authToken.
Now the call works fine and I get a 200 response with the correct info. But I really don´t want to use this method if I don´t have to and according to google I should not need to "•If the request requires authorization (such as a request for an individual's private data), then it must include an OAuth 2.0 token. It may also include the API key, but it doesn't have to." - from developers.google.com/+/api/oauth.
Another thing:
If I try another url like
"https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + authToken
it works fine.
The issue is regarding the simple api key passed into the request.
If the key parameter isn't included in the request, or if the Google+ API wasn't activated for that project, you'll get the error:
"Daily limit exceeded. Please sign up".
To solve this problem, you need to do the following:
Visit the Google API Console here: https://code.google.com/apis/console/?api=plus
Under the Services panel, make sure the Google+ API is turned "on".
In the APIs console, click API Access in the left menu.
Copy the API key presented towards the bottom.
Include this API key in your HTTP request.
GOOGLE_ME_URL + "?access_token=" + authToken + "&key=" + MY_SIMPLE_API_KEY
Most of the newer Google APIs have quotas (like daily usage limits) and some even have billing support (where you get billed per API call). These quotas and billing are calculated per developer's project, and not on a per-end-user basis, so Google needs to know which app to assign your API usage.
API clients using Google's OAuth 2.0 are typically required to register and get a client ID and client secret.
This client ID and client secret are returned by the Google APIs console: code.google.com/apis/console.
You then use these values in your application, and this identifies your app and allows Google to assign your API usage to your developer account/project.
In the AccountManger interface you're using, there is no client ID passed by your app, so Google can't identify which developer account/project's quota to deduct for usage. It also doesn't know that the API has been properly enabled (TOS accepted, etc) by you as a developer. That's why it's asking you to "please sign up" and saying the "Daily limit exceeded" (as the unregistered limit is zero requests for many APIs).
In this scenario, it is necessary for you to pass the "key" value as you did in order to access APIs with OAuth 2.0 tokens retrieved from the AccountManager.
Why don't some of you try to go to the Google Console. In this way you will be able to have access to to the tools you need to rectify at least 403 forbidden problems. DMC
Generate a new auth token and secret for your application and try again.
That might solve your problem, but your daily retry limit might be exhausted...
I had the same issue you're having.
You are using HTTP at the moment, but you are actually calling a the site over HTTPS. Either use a secure connection procedure or use the http:// address.
You just need to enable Google+ API in console.

Categories

Resources