In my Xamarin Android app I call
var instanceID = InstanceID.GetInstance(this);
string token = instanceID.GetToken("xxx", GoogleCloudMessaging.InstanceIdScope, null);
and I get a token in return in the format e63498f:oijafa89fjaasi...
In my c# program I call
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=xxxx");
//Get current connection
string url = "https://gcm-http.googleapis.com/gcm/send";
var message = new JObject();
var data = new JObject();
data.Add("message", "hello from csharp");
message.Add("to", "e63498f:oijafa89fjaasi...");
message.Add("data", data);
HttpResponseMessage response = null;
try
{
response = await client.PostAsync(url, new StringContent(message.ToString(), Encoding.Default, "application/json"));
}
catch (Exception exp)
{
MessageBox.Show(exp.Message);
return;
}
//Handle errors
if (!response.IsSuccessStatusCode)
MessageBox.Show("Error: " + response.ToString());
string responseBody = await response.Content.ReadAsStringAsync();
textBox2.Text = responseBody;
I get the response:
{Text = "{\"multicast_id\":xxxx,\"success\":0,\"failure\":1,\"canonical_ids\":0,\"results\":[{\"error\":\"NotRegistered\"}]}"}
I have tryed alot of things but I cannot get it working. If I use the old GCM (gcm.register) there is no error message, but I don't want to use deprecated functionality. Why does GCM say that the token is not registrered when I just got a token returned from GCM? (The app is of course open while I do the test). Do I need to call some sort of method to actually register the token?
Found this similar thread regarding GCM in the Xamarin forums that discusses the same issue. Mentioned in the thread "The problem seems to be that deployments to the VM may be triggering the uninstall scenario.". A workaround/solution is also included:
"The solution I came up with was to track tokens where I receive a Not Registered response on the server, if a device indicates they want to use that token I respond with a send-a-new-token response. The way to accomplish this is to delete the InstanceId and then trigger the registration service
Google cloud message 'Not Registered' failure and unsubscribe best practices?"
Care to try it out. Let me know if it works.
Related
I'm trying to retrieve a Google user's contacts list.
This getContacts() is called in the doInBackground() of an AsyncTask.
I have -at this point- already received a token.
I based my self on this codeLab sample : https://codelabs.developers.google.com/codelabs/appauth-android-codelab/#0
I'm modifying the point n°10 of this tutorial so trying to fetch user's contact list instead of the user's personal info ( < which is working)
private List<ContactEntry> getContacts() {
ContactsService contactsService = new ContactsService("MY_PRODUCT_NAME");
contactsService.setHeader("Authorization", "Bearer " + token);
try {
URL feedUrl = new URL("https://www.google.com/m8/feeds/contacts/default/full");
Query myQuery = new Query(feedUrl);
ContactFeed resultFeed = contactsService.query(myQuery, ContactFeed.class);
List<ContactEntry> contactEntries = resultFeed.getEntries();
return contactEntries;
} catch (Exception e) {
}
return null;
}
My problem is that I always get an Exception with this message :
java.lang.NullPointerException: No authentication header information
Any help?
thanks in advance
You may refer with this related thead. Try modifying the client library's user
agent:
A workaround is to change the user agent of your client after you create it:
ContactsService service = new ContactsService(applicationName);
service.getRequestFactory().setHeader("User-Agent", applicationName);
Also based from this post, service.setOAuth2Credentials() doesn't refresh token internally like the other services in Google Sites, Google Drive and etc. If you build Google Credential, just add this line after building the Google Credential: googleCredential.refreshToken();.
I'm developing an Android app that consumes data from my own REST API server. I want to use Firebase authentication because it allows the user to login using Google, Facebook, Twitter... in a very simple way.
But I'm not sure how to use ID tokens:
Because ID tokens have expiration date, should I call getToken method on every request in the client app, so I'm sure I'm sending a valid token every time?
Should I call verifyIdToken in the server each time I receive a request from the client app?
I don't know what these methods (getToken and verifyIdToken) do under the hood, and because they are asynchronous, I fear they are doing a request to Firebase servers on every call. So I think that making 2 request to Firebase servers in each of my requests is not the way to go...
Both getToken() and VerifyIdToken() are designed to be called for every outgoing/incoming request.
1) Although getToken() is asynchronous, the Firebase Android SDK actually caches the current Firebase user token in local storage. So long as the cached token is still valid (i.e. within one hour since issued), getToken() returns the token immediately. Only when the cached token expires does the SDK fetch a new token from remote Firebase server.
2) VerifyIdToken() is also optimized for performance. It caches the Firebase token public cert (valid for 6 hours) which is used to validate the token signature on local machine. No RPC is involved except for downloading the public cert.
You refresh token each time when is no more valid. And yes, you should verify token on server-side each time. If is no more valid, you send 401 error code with error message (if you want). Verify token is used when you refresh token, and token is append to each request. If you use OkHttp you can create an interceptor that is adding token in header to each request and also can refresh token when error code is 401.
POST https://YOUR_AUTH0_DOMAIN/delegation
Content-Type: 'application/json'
{
"client_id": "YOUR_CLIENT_ID",
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"refresh_token": "your_refresh_token",
"api_type": "app"
}
From what you have explained in the question, I guess you are talking about cross client resource access using Google sign in. And specifically you seem to be interested in obtaining the Id token once and use it without having to obtain on each subsequent API call.
This more or less is synonymous with the offline access mechanism.
In offline access, the Client I.e. the Android app asks for user authorisation for requested scopes. Upon authorisation, instead of issuing an access token, auth server returns a short lived authorisation code which can be used to generate an access token and refresh token.
The client then can pass the authorisation code to the backend over a secure connection. Backend server can retrieve the author token and refresh token and store them in a secure location. The access token is short lived and can be used to access scoped resources for a short time and refreshed from time to time using the refresh token. The refresh token does not expire but can be revoked. If revoked, server app should ask the client app to re-fetch the author code.
Please go through this link which details the complete infrastructure along with the steps to be followed both by client and server app -
https://developers.google.com/identity/protocols/CrossClientAuth
Now coming to your question, you should use a slightly different API to obtain the auth code. Check out this API -
https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInOptions.Builder.html#requestServerAuthCode(java.lang.String)
Sample code at - https://developers.google.com/identity/sign-in/android/offline-access
Use below code in your application class and regId is the value holder for your device token.
private void checkPlayService() {
// Check device for Play Services APK. If check succeeds, proceed with
// GCM registration.
if (checkPlayServices()) {
GoogleCloudMessaging googleCloudMessaging = GoogleCloudMessaging.getInstance(activity);
regId = getRegistrationId();
if (TextUtils.isEmpty(regId)) {
registerInBackground();
}
} else {
Log.i(TAG, "No valid Google Play Services APK found.");
}
}
private String getRegistrationId() {
String registrationId = sp.getString(Consts.PROPERTY_REG_ID, "");
if (TextUtils.isEmpty(registrationId)) {
Log.i(TAG, "Registration not found.");
return "";
}
// Check if app was updated; if so, it must clear the registration ID
// since the existing regID is not guaranteed to work with the new
// app version.
int registeredVersion = sp.getInt(PROPERTY_APP_VERSION,0);
int currentVersion = getAppVersion();
if (registeredVersion != currentVersion) {
Log.i(TAG, "App version changed.");
return "";
}
return registrationId;
}
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (googleCloudMessaging == null) {
googleCloudMessaging = GoogleCloudMessaging.getInstance(activity);
}
regId = googleCloudMessaging.register(Consts.PROJECT_NUMBER);
msg = "Device registered, registration ID=" + regId;
Log.e("GCMID",msg);
storeRegistrationId(regId);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
Log.i(TAG, msg + "\n");
}
}.execute(null, null, null);
}
private void storeRegistrationId(String regId) {
int appVersion = getAppVersion();
Log.i(TAG, "Saving regId on app version " + appVersion);
sp.edit().putString(Consts.PROPERTY_REG_ID, regId).commit();
sp.edit().putInt(PROPERTY_APP_VERSION, appVersion).commit();
}
I am using Google Cloud Messaging with XMPP in order to have both downstream and upstream messages.
Only client side I get a token by doing this on a worker thread:
InstanceID instanceID = InstanceID.getInstance(this);
try {
String token = instanceID.getToken(getString(R.string.gcm_senderID), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
send_token(token, getString(R.string.gcm_senderID));
} catch (IOException e) {
e.printStackTrace();
}
I then send this token over to the server where it is received. I am able to send messages to the client with this token.
Then I can send an upstream message on the client side with this:
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg;
Bundle data = new Bundle();
data.putString("message", message);
try {
messenger.send(getString(R.string.gcm_senderID) + "#gcm.googleapis.com", messageId.addAndGet(1) + "", data);
} catch (IOException e) {
e.printStackTrace();
}
msg = "Sent message";
return msg;
}
}.execute(null, null, null);
In the upstream message sent from the client, there is a from field, that seems to be a token as well. If I send a message to this from the server side, my phone receives it as well.
What confuses me is that the token in the from field is not equal to the one generated by the InstanceID service.
The first 18 characters or so are equal, but after that they are very different. As such, is there a good way to identify what device sent what message?
I could store the token generated by the Instance ID each time in the Bundle, but I was wondering if there might be any way to make the from field of the upstream message be consistent with the generated ID.
Edit: Using the deprecated register function, I was able to get a consistent registration ID.
String token = GoogleCloudMessaging.getInstance().register(getString(R.string.gcm_senderID));
But is there a way to do this with InstanceID?
Calling GoogleCloudMessaging.getInstance(context).register(senderId) instead of getToken(senderId, "GCM") seems to resolve the issue, the XMPP server will then receive the correct token, every time, in the "from" property of the upstream message.
My device is running CyanogenMod, so the Google Play services app doesn't update automatically. Since the old register() work, this issue is likely caused by a bug in the google-play-services_lib when talking to an older version of the GMS app.
I've answered instead of comment with the vain hopes of an Google dev seeing this.
I am using the Amazon Web Service to send push notifications directly to a device. After I install the app I get the device id, that I need to manually add to the Amazon SNS. I would like to know if there is anyway to register the device id directly with the amazon server the moment the user starts the application.
I have read this, but found it difficult to understand. Does anyone have any previous experience of how to do this?
EDIT 2 (What I have done so far)
I've followed the instructions from this link
I download the snspobilepush.zip file as instructed and extract and import the project into eclipse. I add the GCM project number, add the jar files and run the application. I get my device registration Id.
I open the Amazon SNS, add my device id and I publish a message. I receive the message on my mobile phone. Works great so far.
MY PROBLEM
I would be having a lot of potential users for my application. So adding every device id manually to the SNS makes no sense. I need the Amazon SNS to directly register my device id when I start the app. Is there any option for me to do that? I couldn't find any definitive answer in the docs.
This link tells me to Use the "AWS Token Vending Service". However, I could not find any example of how to do that.
Using the AmazonSNSClient documented here:
http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/
it should be possible to register using code similar to this:
AWSCredentials awsCredentials = new BasicAWSCredentials("XXXXXX", "XXXXXXXXXXXXXXX");
String platformApplicationArn = "arn:aws:sns:us-east-1:123456789:app/GCM/myappname";
AmazonSNSClient pushClient = new AmazonSNSClient(awsCredentials);
String customPushData = "my custom data";
CreatePlatformEndpointRequest platformEndpointRequest = new CreatePlatformEndpointRequest();
platformEndpointRequest.setCustomUserData(customPushData);
platformEndpointRequest.setToken(pushNotificationRegId);
platformEndpointRequest.setPlatformApplicationArn(platformApplicationArn);
CreatePlatformEndpointResult result = pushClient.createPlatformEndpoint(platformEndpointRequest);
Log.w(TAG, "Amazon Push reg result: " + result);
It was not liking my ARN, but that was a stupid typo that Reid pointed out and is now fixed above.
There is Android AWS SDK available to use. Check out the documentation link: http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/
Also, more information available: http://aws.amazon.com/sdkforandroid/
This is working for Firebase and Cognito. An AsyncTask is necessary to avoid running on the Main Thread.
private class RegisterIdForAWS extends AsyncTask<String, Void, Void> {
private Exception exception;
protected Void doInBackground(String... urls) {
try {
String pushNotificationRegId = FirebaseInstanceId.getInstance().getToken();
if (pushNotificationRegId != null) {
CognitoCachingCredentialsProvider provider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"us-west-2:aaaaaaaaa-1234-1234-1234-0bbbbbbbbbbbb",
Regions.US_WEST_2);
String platformApplicationArn = "arn:aws:sns:us-west-2:123456789:app/GCM/appname";
AmazonSNSClient pushClient = new AmazonSNSClient(provider);
pushClient.setRegion(Region.getRegion(Regions.US_WEST_2));
String customPushData = "";
CreatePlatformEndpointRequest platformEndpointRequest = new CreatePlatformEndpointRequest();
platformEndpointRequest.setCustomUserData(customPushData);
platformEndpointRequest.setToken(pushNotificationRegId);
platformEndpointRequest.setPlatformApplicationArn(platformApplicationArn);
CreatePlatformEndpointResult result = pushClient.createPlatformEndpoint(platformEndpointRequest);
Log.w(TAG, "Amazon Push reg result: " + result);
}
} catch (Exception e) {
this.exception = e;
}
return null;
}
protected void onPostExecute(String text) {
Log.w(TAG, "Amazon Push reg Finished");
}
}
I need to obtain OAuth2 authentication token to pass it to the server so it can fetch list of Google Reader feeds for the user. Server is .NET - I have no access to it or to it's code but most likely it is using unofficial Reader API
I was able to use Android Account manager to obtain valid token for this purpose with the following code (notice that authTokenType="reader")
Account account = accounts[0];
manager.getAuthToken(account, "reader", null, this, new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
try {
// If the user has authorized your application to use the tasks API
// a token is available.
String token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN);
// Now you can send the token to API...
cacheManager.putString(GOOGLE_AUTH, token);
GoogleReaderManager.startAddFeedActivity(AddGoogleReaderSourcesActivity.this);
finish();
} catch (OperationCanceledException e) {
Log.e(TAG, "User cancelled", e);
finish();
} catch (Exception e) {
Log.e(TAG, "Failed to obtain Google reader API_KEY", e);
}
}
}, null);
The code above works fine when I send token to the server side .Net app: the app is able to retrieve the list of Reader feeds.
The problem is that this only works for "Google inside" devices. On Nook I have no such luck since there's no way that I was able to find to add Google account to the account manager. So I'm trying to it using OAuth 2 protocol as described here
It works fine as far as obtaining the token: User approves the app from the mobile page which returns the code token which then mobile app exchanges for the Auth token. However this token will not work with the server process. I have a feeling that perhaps I'm using the wrong scope in this URL:
https://accounts.google.com/o/oauth2/auth?response_type=code&scope=https://www.google.com/reader/api/0/subscription/list&redirect_uri=http://localhost&approval_prompt=force&state=/ok&client_id={apps.client.id}
Scopes that I did try in various combinations:
https://www.google.com/reader/api
https://www.google.com/reader/api/0
https://www.google.com/reader/api/0/subscription/list
https://www.google.com/reader/api+https://www.google.com/reader/atom
Here's example of JSON that is returned from get token POST
{"expires_in":3600,
"token_type":"Bearer",
"access_token":"ya29.AHES6ZSEvuUb6Bvd2DNoMnnN_UnfxirZmf_RQjn7LptFLfI",
"refresh_token":"1\/bUwa5MyOtP6VyWqaIEKgfPh08LNdawJ5Qxz6-qZrHg0"}
Am I messing up scope or token type? Not sure how to change a token type. Any other ideas?
P.S. Google account login page asks: Manage your data in Google Reader, that's why I suspect that the scope is wrong
I got it working for https://www.google.com/reader/api/0/subscription/list. So thought of sharing with you.
I have valid access_token:
This is what i tried to resolve it (partially) :
Google provides OAuth 2.o playgound; where they actually simulate all aspects of OAuth 2.0 as well as final API call to fetch data.
I found this very helpful as it clearly shows what is being sent to request.
Here is the URL : https://developers.google.com/oauthplayground/
Using this, i tweaked my api call below and it works :)
public static String getReaderContent(String accessToken){
String url = "https://www.google.com/reader/api/0/subscription/list" ;
HttpClient client = new HttpClient();
GetMethod method = new GetMethod(url);
String response="";
method.setRequestHeader("Authorization", "OAuth "+accessToken);
try {
int statusCode = client.executeMethod(method);
String response= method.getResponseBodyAsString();
System.out.println("response " + responseStr);
} catch (HttpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
So this works properly fine for getting subscription list; but have not been able to make it work for reader api which you have mentioned in your question.
Let me know if you have got way around google reader API.