Firebase Token is inconsistent between API calls - android

I am using Firebase FCM to send notifications from my server to the users.
When the user installs the App for the first time, i catch the fresh and new token at MessagingService.java:
#Override
public void onNewToken(#NonNull String tkn) {
super.onNewToken(tkn);
sendTokenToServer(tkn);
}
Here comes the problem, when the user closes session (without uninstalling the app), SharedPreferences are deleted. A new session is started; but onNewToken() is not called. So, i must manually retrieve the Token inside my MainActivity in order to send it to the server. I am getting the updated token with this piece of code:
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(MainActivity.this, new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
sendTokenToServer(instanceIdResult.getToken());
}
});
As you know, that code is deprecated and should be avoided. Instead, i tried to replace it with this piece of code with no success:
FirebaseInstallations.getInstance().getToken(true).addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
#Override
public void onComplete(#NonNull Task<InstallationTokenResult> task) {
if(task.isSuccessful()) {
String token = task.getResult().getToken();
}
}
});
The Token length obtained at onNewToken() is 163.
The token length obtained at deprecated call is 163 (perfect, but deprtecated).
The token length obtained at FirebaseInstallations is 316.
My firebase API at server side fails to send a notification using the code of 316 length.
Any one knows what i am doing wrong? Or why i get those different length tokens?
Update:
Server side python, retrieves token from database and sends the notification like this. Please note this code is working when token len is 163.
from pyfcm import FCMNotification
push_service = FCMNotification(api_key=" ... ")
push_service.notify_single_device(registration_id=token, data_message=datamessage, time_to_live=1296000)
When trying to send a notification with long token this is the message I get:
{'multicast_ids': [8149274412512777031], 'success': 0, 'failure': 1, 'canonical_ids': 0, 'results': [{'error': 'InvalidRegistration'}], 'topic_message_id': None}

From the documentation for FirebaseInstanceId:
This class is deprecated. Firebase Instance ID has been replaced with
FirebaseInstallations for app instance identifiers and
FirebaseMessaging.getToken() for FCM registration tokens.
Looks like you need FirebaseMessaging.getToken() not FirebaseInstallations.getInstance().getToken(true) as you want a FCM registration token.
These APIs provide different tokens for different purposes.
So in your example it would be:
FirebaseMessaging.getInstance()
.getToken()
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override public void onComplete(#NonNull Task<String> token) {
}
}
);

Related

Unable to find FCM token

I am trying to implement notifications and can send a notification using the firebase console to all devices of the app, however I am having problem trying to retrieve the token of a device so i can send the notification to one particular device. I have a service that extends FirebaseMessagingService and includes the method onNewtoken seen below. I have added this service to my manifest and tried running the app but still unable to find the token. Is there something im doing wrong?
#Override
public void onNewToken(#NonNull String s) {
super.onNewToken(s);
Log.d("NEW_TOKEN",s);
}
OnNewtoken will trigger only by following below scenarios
The app deletes Instance ID
The app is restored on a new device
The user uninstalls/reinstalls the app The user clears app data.
you can get user token by below code as well
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
String token = instanceIdResult.getToken();
// send it to server
}
});
You can get token forcefully like this u can call this code from onCreate() as sometimes when app has already generated to token it does not call onNewToken
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
#Override
public void onComplete(#NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
KLog.w("getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
if (task.getResult() != null) {
String token = task.getResult().getToken();
}
}
});

How to send to server when refresh token?

How to check this condition and then send to server when refresh token?
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
Here is my code to send server. But is it send all time when app login ? How to send when above condition?
#Override
public void onNewToken(String s) {
super.onNewToken(s);
Log.e("NEW_TOKEN", s);
sendRegistrationToServer(s);
}
private void sendRegistrationToServer(String token) {
APIInterface apiInterface = APIClient.getClient().create(APIInterface.class);
Call<Fcm> call = apiInterface.postFCMToken(user_id, newToken);
call.enqueue(new Callback<Fcm>() {
#Override
public void onResponse(Call<Fcm> call, Response<Fcm> response) {
}
#Override
public void onFailure(Call<Fcm> call, Throwable t) {
}
});
}
In general, you should expect that an app installation is unable to reliably detect when an FCM token changes. As such, any new token should be unconditionally reported to the backend, as a potential target for messaging for the given user.
The server side of your app can remove invalid tokens by looking at the error response when attempting to send a message to a token that's no longer valid. If you're using the Admin SDK to send a message to a device token, you can detect then when a token is invalid because the API will generate the error messaging/registration-token-not-registered, according to the documentation.

Android + Firebase Auth + REST API: How to properly work with token?

I'm starting a new project and Firebase Auth was the choice for authentication. The idea is to create/login users through Firebase Auth and then, use the Firebase ID Token to authenticate on my backend (through Authentication header).
In Google Samples, this is the way I should get the token:
FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();
mUser.getIdToken(true)
.addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
public void onComplete(#NonNull Task<GetTokenResult> task) {
if (task.isSuccessful()) {
String idToken = task.getResult().getToken();
// Send token to your backend via HTTPS
// ...
} else {
// Handle error -> task.getException();
}
}
});
But, as you can see, this is a async call, because it goes to Firebase servers to get me the Token. So, every REST API call to my backend, I need to run the above code, because I don't know when the token has expired.
Is there a better way to safety call my backend REST APIs using Firebase Auth? Or using the Firebase ID Token is the best one? If so, how should I wrap this id token get for every REST API call?
If you have a better way to authenticate users to call rest apis later, I'm all ears.
so the Idea is quite simple. You can use this method in the Android device as you already know:
FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();
mUser.getIdToken(true)
.addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
public void onComplete(#NonNull Task<GetTokenResult> task) {
if (task.isSuccessful()) {
String idToken = task.getResult().getToken();
// Send token to your backend via HTTPS
// ...
} else {
// Handle error -> task.getException();
}
}
});
Once you hold the instance of the token you send it to your backend for authentication there, it will authenticate only once and send you backend a token that is managed by the backend itself. That's the token that will be sent in each subsequent call to the backend, not the generated by the above method.

I want to call FCM manually to get token while it is null in android application

I wanna know that if there is solution to get FCM token while it's null? In other words, I want to call FCM manually to get token while it is null in Android application?
If you want to get the token faster you have to use:
token = FirebaseInstanceId.getInstance().getToken("projectNumber","FCM");
instead of:
token = FirebaseInstanceId.getInstance().getToken();
Use getInstanceId() instead of getToken(),its deprecated.
new Thread(new Runnable() {
#Override
public void run() {
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
String deviceToken = instanceIdResult.getToken();
}
});
}
}).start();
If you get null when calling FirebaseInstanceID.getToken(), it means that the token hasn't been generated yet. There is no API to force generating the token now, so you'll have to instead monitor token generation.
To monitor token generation, create a class that derives from com.google.firebase.iid.FirebaseInstanceId and override its onTokenRefresh() method:
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(refreshedToken);
}
Note that both getting and monitoring the token are covered in the Firebase documentation on accessing the registration token, which is where I got the above code sample from.

Firebase FCM token - When to send to server?

Okay so I have an app which on first start takes you through a few welcoming slides, then takes you to a login/register page and then to MainActivity.
I have just implemented FCM and the services generate a token before any of those pages have been seen by the user. How could I make it so that the service runs after I get to MainActivity?
The problem is I'm trying to send the token as soon as it is refreshed to the MySQL DB to the appropriate user account, but since the user hasn't signed in yet, that is null and my message to the server fails. What's a good way to design this? I thought of saving the token in SharedPreferences and sending it to the server after the user has logged in but that creates lots of complications when the token is refreshed at some later point?!
Possible solution:
I'm not sure I completely understand how the 2 services run but say in onTokenRefresh I just save the token into SharedPreferences and in MainActivity I get the value from SP and then I send it to the server. In that case when the token is refreshed the new value will immediately go into SharedPreferences again. But I would still need to check if it's a new value in SP and then reupload it to the server. This is confusing!
Note that you can always retrieve the token with:
FirebaseInstanceID.getInstance().getToken();
This will return null if the token has not yet been generated or the token if it has been generated. In your case it is very likely that the token will be generated by the time the user has signed in. So you should be able to send it to your app server as soon as the user has signed in. If it is not available then you would send it in the onTokenRefresh callback as Chintan Soni mentioned.
Edit
Using the new Firebase SDK (21.0.0) , you will get your token this way :
FirebaseInstallations.getInstance().getToken(false).addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
#Override
public void onComplete(#NonNull Task<InstallationTokenResult> task) {
if(!task.isSuccessful()){
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
}
});
You better add a listener for more handling on the response .
Yes FCM token is generated automatically. But try to see this in a different angle.
This is how I handled it.
Let FCM generate token as soon as your app starts. OnTokenRefresh will be called and you just save it in your preferences as:
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
sendRegistrationToServer(refreshedToken);
}
private void sendRegistrationToServer(String token) {
// Add custom implementation, as needed.
SharedPreferenceUtils.getInstance(this).setValue(getString(R.string.firebase_cloud_messaging_token), token);
// To implement: Only if user is registered, i.e. UserId is available in preference, update token on server.
int userId = SharedPreferenceUtils.getInstance(this).getIntValue(getString(R.string.user_id), 0);
if(userId != 0){
// Implement code to update registration token to server
}
}
Hope you are clear with the way. Ask if you need more clearance on it.
Edit
Using the new Firebase SDK (21.0.0) , you need to override onNewToken() method instead of onTokenRefresh()
#Override
public void onNewToken(#NonNull String s) {
super.onNewToken(s);
sendRegistrationToServer(s);
}
We handled it like this:
Our server create/update the token value against a user id (primary key)
Use 2 SharedPreferences
String - token String
Boolean (updated) - whether token is updated on server or not.
In case of token refresh we update the token string and set the boolean to false.
Later whenever user login each time we check for boolean (updated), if that is false - we attach the current token to his id and send it to server and set updated to true.
December 2020 update : Using the new Firebase SDK (21.0.0) you can get it by overriding onNewToken() method :
#Override
public void onNewToken(#NonNull String s) {
super.onNewToken(s);
sendRegistrationToServer(s);
}
Or by FirebaseInstallations.getInstance() within your scope :
FirebaseInstallations.getInstance().getToken(false).addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
#Override
public void onComplete(#NonNull Task<InstallationTokenResult> task) {
if(!task.isSuccessful()){
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
}
});

Categories

Resources