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. :-)
Related
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
We are successfully using the Legacy HTTP Server Protocol on our server for FCM.
I wanted to update to FCM HTTP v1 API today.
I did it step by step and when the server calls the request, we get this response:
Server returned HTTP response code: 403 for URL: https://fcm.googleapis.com/v1/projects/[projectid]/messages:send
This is the server code:
URL url = new URL("https://fcm.googleapis.com/v1/projects/[projectid]/messages:send");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Authorization", "Bearer " + getAccessToken());
conn.setRequestProperty("Content-Type", "application/json");
OutputStream outputStream = conn.getOutputStream();
outputStream.write(req.getBytes("UTF-8"));
// Exception happen here
InputStream inputStream = conn.getInputStream();
The getAccessToken():
private static String getAccessToken() throws IOException {
GoogleCredential googleCredential = GoogleCredential
.fromStream(new FileInputStream(ClientApiServlet.context.getRealPath("/WEB-INF/[projectid].json"))) .createScoped(Arrays.asList("https://www.googleapis.com/auth/firebase.messaging"));
googleCredential.refreshToken();
return googleCredential.getAccessToken();
}
I have downloaded the json file from the adminsdk page of the firebase cloud.
All with the same projectid...
I updated these 2 libs on the server:
google-http-client-jackson2-1.23.0.jar
google-oauth-client-1.23.0.jar
The getAccessToken() methode returned an accesstoken: "ya29.c.Elr0BAa..."
I think, I miss a small step, maybe you could help?
Thanks in advance!
Edit:
It is working now with the hint of arterpa! Thanks again!
After that I got a 400 error, so something in the request data was wrong:
The problem was, we didn't converted all data{...}values to strings. With the legacy protocol it was not an issue, but with FCM HTTP v1 API it has to be strings! ;)
I had this problem, and it seems you need to enable FCM API for your project at Google API console.
I was having this issue of getting a 403 from the FCM HTTP v1 API. I had FCM Messaging API enabled, but my problem was that the service account I was using didn't have the correct role to make requests to the FCM API. You can try creating a new service account with the "project Owner" role to see if that helps.
I'm working on an Android app that implements the Spotify API to allow the users to listen to music. I've configured the Player that Spotify has created for android devices, but it's incredibly limited in terms of its functionality, so I've had to go through Spotify's Web API to do more advanced features.
I've hit a bug when trying to get a list of the user's own playlists. I'm making a request using:
URL url = new URL("https://api.spotify.com/v1/me/playlists");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
But instead of this command going through like it does for the other web API requests I've made, it throws the error:
java.io.FileNotFoundException: https://api.spotify.com/v1/me/playlists
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:242)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:25)
at com.tmacstudios.spotifyvoice.WebWrapper$override.searchUserPlaylist(WebWrapper.java:257)
at com.tmacstudios.spotifyvoice.WebWrapper$override.access$dispatch(WebWrapper.java)
at com.tmacstudios.spotifyvoice.WebWrapper.searchUserPlaylist(WebWrapper.java:0)
at com.tmacstudios.spotifyvoice.MainActivity$6.run(MainActivity.java:382)
at java.lang.Thread.run(Thread.java:818)
I'm not entirely sure why I'm getting this error, but I think it may have to do with not having the necessary authorization to make this request. Can anyone please help me solve this problem?
After studying the documentation some more, I was able to figure out a way to solve my problem. I was in fact getting the error since I wasn't using the proper authorization.
You can get the authorization token in OnActivityResult using this code:
AuthenticationResponse response = AuthenticationClient.getResponse(resultCode, intent);
if (response.getType() == AuthenticationResponse.Type.TOKEN) {
authToken = response.getAccessToken();
Log.e("MainActivity","Auth Token: "+authToken.toString());
...
Then when making the URL request, just pass in the authorization token as a header.
URL url = new URL("https://api.spotify.com/v1/me/playlists");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestProperty("Authorization", "Bearer " + authToken);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
...
Also note that you must have the permissions you are using enabled as scopes when you are forming the authorization request initially.
I am having some issues in getting Google authentication to work in my android app, using Google Play Services.
First, I used the samples provided with the library to obtain a token using the scope bellow
GoogleAuthUtil.getToken(getActivity(), mEmail,"oauth2:https://www.googleapis.com/auth/userinfo.profile");
then I used the token to get more info about the user:
URL url = new URL("https://www.googleapis.com/oauth2/v1/userinfo?access_token=" + token);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
That worked. Then, I used Verifying Back-End Calls from Android Apps post on Google Developers Blog to authenticate the app users to my web server. So I replaced the scope of the getToken with
audience:server:client_id:<webapp_clientId_for_localhost>.apps.googleusercontent.com
and used a similar checker with the one in the article to verify the audience and clientId (Android Client Id) on the server side. This also worked but, if I try to get more info about the user (first and last name), either from the android app or from the web app, using the userinfo API I get:
{ "error": { "errors": [ {
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization" } ], "code": 401, "message": "Invalid Credentials" } }
The code I use is:
URL url = new URL("https://www.googleapis.com/oauth2/v1/userinfo");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Authorization", "OAuth " + token);
con.setRequestProperty("ContentType", "application/json; charset=UTF-8");
Any idea why userinfo not working? Is there something else I should add to the scope?
The token you use for backend verification is intended for your server, while the one with oauth2: is intended for Google. At this point in time there's no mechanism to request both tokens simultaneously, so you need to make two calls to GoogleAuthUtil. You should be able to request both (one at a time) after a single consent step. In practice, the oauth2: token should be needed less frequently, as you can associate the user's profile data w/ the account you have from them in your home server.
I followed this Push Notification tutorial.
When I finish the tutorial, I found out that two classes did not use which were AuthenticationUtil and MessageUtil.
Moreover, Google Login, this link seem unworkable. Second, This is the token id for the Android device or the account only? i thought push notification is push message to token id of Android device.
On the others hand, i found out that the bundle.putExtra(key, value), all the keys did not use it. For example put "app" but in C2DMRegistrationReceiver() did not get the key.
In this sendRegistrationIdToServer(), it seem like never being call out.
I am being confused by this tutorial about push notification.
Who can guide me or give me workable tutorial or example about push notification?
I would like pro to point out what's my wrong.
This is my registration id
public static final String[] REGISTRATION_ID = {
"APA91bFV6MwoAH0UNop69PZ2liKpSBUHSHenIuPzh44_6GdGKzVCLvoH_NM31eMZMVLZi-SAIFwP4iZaE72dSWkIh3GaD0RQYpPm9zO0ARWmnoxFyyyreL_KpQ9Qd_p0broclT12RhA4Ymk0cBT00CmpsbSHIwyxig",
"APA91bEwmxgvs7zNbKC4p0n4DoTEM73DTihnQgBOP8Gxhf2sVW-fgltugDgS1Fh2S4KvN1wQHbMNJEIzieJ9F1nNPqs3NWeKGbB7IBYpKJq4xmN4Z7uzkjZQQUKGD8jW--AwfQY5McINBto9GAL_87_u5WkIq-kx3g",
"APA91bH63Zgxn1X_MZ56UzrlRpffvmiLAIsqxvBUTMUHP2O_MT_VU9Ork_edXKHlml-PZSkjKEqdk8EKv5HvxbPdK1Vva3WtmqsPZfhXzEbtNIrwrqIvvRf7hL835rDc4t2E8EKUBj1dX2ta0OxY5pY3Xlhkyb1sBg",
"APA91bGqT5Wo6eUaMdqT5r9TlGbKSX6GN2W6r-RjrRXz5T5v3j87flcQRyfSajmMNGXuPVe-fwZydRmvyYu63tWnYohDmpJyKkXOxs8Vx6P_FplFQ__ufR_hekwqGOspeUc6bfc8fhbMPGN3Ft9l-bfrghJwwk79jw"};
Messageutil
public static int sendMessage(String auth_token, String registrationId,
String message, String title) throws IOException {
StringBuilder postDataBuilder = new StringBuilder();
postDataBuilder.append(PARAM_REGISTRATION_ID).append("=")
.append(registrationId);
postDataBuilder.append("&").append(PARAM_COLLAPSE_KEY).append("=")
.append("0");
postDataBuilder.append("&").append("data.payload").append("=")
.append(URLEncoder.encode(message, UTF8));
postDataBuilder.append("&").append("data.title").append("=")
.append(URLEncoder.encode(title, UTF8));
byte[] postData = postDataBuilder.toString().getBytes(UTF8);
// Hit the dm URL.
URL url = new URL("https://android.clients.google.com/c2dm/send");
HttpsURLConnection
.setDefaultHostnameVerifier(new CustomizedHostnameVerifier());
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
conn.setRequestProperty("Content-Length",
Integer.toString(postData.length));
conn.setRequestProperty("Authorization", "GoogleLogin auth="
+ auth_token);
OutputStream out = conn.getOutputStream();
out.write(postData);
out.close();
int responseCode = conn.getResponseCode();
return responseCode;
}
private static class CustomizedHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
Messagesender
public static void main(String[] args) throws IOException {
String token = AuthenticationUtil.getToken(SecureStorage.USER,
SecureStorage.PASSWORD);
for (int i = 0; i < ServerConfiguration.REGISTRATION_ID.length; i++) {
MessageUtil.sendMessage(token,
ServerConfiguration.REGISTRATION_ID[i], "12358",
"印尼羽賽:馬2單1雙止步入選賽");
System.out.println(ServerConfiguration.REGISTRATION_ID[i]
.toString());
}
System.out.println(token);
}
You should follow this tutorial for android c2dm implementation.
For server, you could use anything, some code sample available on internet. For server I used .NET library called "C2DM Sharp"
The process is very simple like...
First register your google email for c2dm on - https://developers.google.com/android/c2dm/signup
Run the android application on Android 2.2 or higher and send the registrationID which you can get in "C2DMReceiver" or get that ID by writting in LOG
Use the server code, for testing purpose paste your registrationID in Server code and you are ready to go.
The basic flow of C2DM is ...
Register Phone for C2DM -> Get registrationID -> Send registrationID to server -> Server usees google id to get auth token -> server use registrationID and auth token to send message.
Google Cloud Messaging for Android
Important: C2DM(Android Cloud to Device Messaging Framework) has been officially deprecated as of June 26, 2012. This means that C2DM has stopped accepting new users and quota requests. No new features will be added to C2DM. However, apps using C2DM will continue to work. Existing C2DM developers are encouraged to migrate to the new version of C2DM, called Google Cloud Messaging for Android (GCM). See the C2DM-to-GCM Migration document for more information. Developers must use GCM for new development.
Kindly check the following link:
http://developer.android.com/guide/google/gcm/index.html
Please see my question here:
C2DM server. Should we use OAuth now?
There is some info and link to google group with answer.
In short..
Seems like OAuth2 will work, but I didn't find any working sample to implement
Client Login works and this is place where my confusion was. You need to:
Set up google account. I picked something like mynamec2dm#gmail.com.
Register for C2DM using this email. This is important.
On server-side use email/password you setup to get auth token.
Use this token to send messages from server.
Everything else is just like in all tutorials around.