When do FCM tokens expire? Is it at 6 months?
It doesn't expire though. It renews itself if one of the following happens.
According to https://firebase.google.com/docs/cloud-messaging/android/client:
-The app deletes Instance ID
-The app is restored on a new device
-The user uninstalls/reinstall the app
-The user clears app data.
Monitor token generation
The onTokenRefreshcallback fires whenever a new token is generated, so
calling getToken in its context ensures that you are accessing a
current, available registration token. Make sure you have added the
service to your manifest, then call getToken in the context of
onTokenRefresh, and log the value as shown:
#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);
}
EDIT
onTokenRefresh() is now deprecated. onNewToken() should be used instead.
As stated in the documentation here the token doesn't expire it only changes on certain events. Whenever a new token is generated a method onTokenRefereshId is called.To implement this create a class which extends FirebaseInstanceIdService and override the onRefreshToken as follows:
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
#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);
}
}
Also do not forget to register this service in the manifests
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
Using firebase admin, you can do this:
async function isValidDeviceToken (deviceToken) {
const {
results: [notifResult]
} = await firebaseAdmin.messaging().sendToDevice(
deviceToken,
{
notification: {
title: 'Device Registration',
message: 'Your device has been registered.'
}
},
{
dryRun: true
}
);
// returns true if valid, false if not.
return !notifResult.error;
}
what it does is it will check if the provided deviceToken is valid, behid the scene, firebase admin checks if the deviceToken is registered. If it is not registered, it will return the following error:
{
error: FirebaseMessagingError: The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages.
at FirebaseMessagingError.FirebaseError [as constructor] (/Users/aprilmintacpineda/projects/my-app/node_modules/firebase-admin/lib/utils/error.js:44:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/Users/aprilmintacpineda/projects/my-app/node_modules/firebase-admin/lib/utils/error.js:90:28)
at new FirebaseMessagingError (/Users/aprilmintacpineda/projects/my-app/node_modules/firebase-admin/lib/utils/error.js:256:16)
at Function.FirebaseMessagingError.fromServerError (/Users/aprilmintacpineda/projects/my-app/node_modules/firebase-admin/lib/utils/error.js:289:16)
at /Users/aprilmintacpineda/projects/my-app/node_modules/firebase-admin/lib/messaging/messaging.js:105:63
at Array.forEach (<anonymous>)
at mapRawResponseToDevicesResponse (/Users/aprilmintacpineda/projects/my-app/node_modules/firebase-admin/lib/messaging/messaging.js:101:26)
at /Users/aprilmintacpineda/projects/my-app/node_modules/firebase-admin/lib/messaging/messaging.js:370:24
at processTicksAndRejections (node:internal/process/task_queues:94:5)
at async isValidDeviceToken (/Users/aprilmintacpineda/projects/my-app/test.js:13:7) {
errorInfo: {
code: 'messaging/registration-token-not-registered',
message: 'The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages.'
},
codePrefix: 'messaging'
}
}
Related
I am working on firebase FCM to send message from device to device. However, I am stuck at retrieving token ID. I have tried with " FirebaseInstanceId.getInstance().getToken();" but not able to get token ID. It keeps returning me null value.
I found some post saying that getToken() is deprecated, have to use getInstanceId() instead. However, my android studio failed to resolve this getInstanceId() and onNewToken(). Is there any way to get firebase TokenID?
Access the device registration token:
On initial startup of your app, the FCM SDK generates a registration token for the client app instance. If you want to target single devices, or create device groups, you'll need to access this token.
You can access the token's value by creating a new class which extends FirebaseInstanceIdService . In that class, call getToken within onTokenRefresh , and log the value as shown:
#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);
}
Also add the service to your manifest file:
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
The onTokenRefresh callback fires whenever a new token is generated, so calling getToken in its context ensures that you are accessing a current, available registration token. FirebaseInstanceID.getToken() returns null if the token has not yet been generated.
After you have obtained the token, you can send it to your app server. See the Instance ID API reference for full detail on the API.
(From firebase reference)
In FirebaseMessagingService class add below code.
#Override
public void onNewToken(String s) {
super.onNewToken(s);
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
String BMrefreshedToken = instanceIdResult.getToken();
Log.e("newToken",BMrefreshedToken);
}
});
}
I'm using firebase notifications. There are some times when I don't get the pushtoken on time. How can I wait for it?
pushToken = firebaseIDService.getToken();
//SOME CODE
registerUser(pushToken);
So, I just want to stop that function until I get the result of the getToken().
Thanks.
The token is generated asynchronously, and may be refreshed periodically. To ensure your app uses the latest token, implement FirebaseInstanceIdService .onTokenRefresh as shown in the documentation on monitoring token generation:
The onTokenRefreshcallback fires whenever a new token is generated, so calling getToken in its context ensures that you are accessing a current, available registration token. Make sure you have added the service to your manifest, then call getToken in the context of onTokenRefresh, and log the value as shown:
#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);
}
The sendRegistrationToServer function in the above snippet is something you'd implement yourself, so likely equivalent to registerUser in your code
Warning! Don't use it on main thread!
Also, it's a bad practice (probably), better use answer by #FrankvanPuffelen.
And I'm not sure it's working, but I'm using same approach for few async operations.
#WorkerThread
public String getTokeSync(Context context) {
String token = FirebaseInstanceId.getInstance().getToken();
if (token != null)
return token;
IntentFilter filter = new IntentFilter();
filter.addAction("com.google.android.c2dm.intent.RECEIVE");
filter.addCategory(BuildConfig.APPLICATION_ID);
final CountDownLatch lock = new CountDownLatch(1);
final FirebaseInstanceIdReceiver br = new FirebaseInstanceIdReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
token = FirebaseInstanceId.getInstance().getToken();
lock.countDown();
context.unregisterReceiver(this);
}
}
context.registerReceiver(br, filter);
lock.await(); // Better use await with timeout
return token;
}
I am using FCM to provide notifications in my app. Everything worked well, but now I realised that, when I install my application using Android Studio (not from GooglePlay) the token is null at first run. When I close my app and restart it, the token is not null anymore. What cause this problem and how can I avoid it?
InstanceIDService:
public class InstanceIDService extends FirebaseInstanceIdService {
#Override
public void onTokenRefresh() {
String token = FirebaseInstanceId.getInstance().getToken();
registerToken(token);
}
private void registerToken(String token) {
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("Token",token)
.build();
Request request = new Request.Builder()
.url("url_to_registration_script")
.post(body)
.build();
try {
client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}
In MainActivity:
FirebaseMessaging.getInstance().subscribeToTopic("topic");
String token = FirebaseInstanceId.getInstance().getToken();
Log.d("TOKEN", token);
Last log returns null when app is installed and started for the first time
Registration script:
<?php
if (isset($_POST["Token"])) {
$_uv_Token=$_POST["Token"];
$conn = mysqli_connect("localhost","user","pass","db") or die("Error connecting");
$q="INSERT INTO users (Token) VALUES ( '$_uv_Token') "
." ON DUPLICATE KEY UPDATE Token = '$_uv_Token';";
mysqli_query($conn,$q) or die(mysqli_error($conn));
mysqli_close($conn);
}
?>
The token is fetched asynchronously on first app start. You have to wait for onTokenRefresh() to be called in your FirebaseInstanceIdService before the token can be accessed.
uninstall the app from emulator and run again , so that the onTokenRefreshed method will be called again .
To check wether you are already registered and you just want to know the FCM TOken
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
add the above line in MyFirebaseMessagingService class any where(in oncreate method ) and just Toast or log the refreshedToken.
Just replace:
String token = instanceID.getToken();
with:
String token = instanceID.getToken($SENDER_ID, "FCM");
and it will work.
I was facing the same problem. I looked through a lot of SO posts and other forums and I found a solution that worked for me. FCM documentation says to use this method to get a token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
I found a post online (I apologize, I don't remember which one. I found it a while ago) that used this method instead:
String refreshedToken = FirebaseInstanceId.getInstance().getToken() (String authorizedEntity, String scope);
FCM documentation describes the first method as:
Return the master token for the default Firebase project.
While the second one is described as:
Returns a token that authorizes an Entity to perform an action on behalf of the application identified by Instance ID.
This is similar to an OAuth2 token except, it applies to the application instance instead of a user.
For example, to get a token that can be used to send messages to an application via FirebaseMessaging, set to the sender ID, and set to "FCM".
I have been looking into why the first method call takes a longer time to return a token, but I haven't found an answer yet. Hope this helps.
Even I had the same issue. I had given the Log statement to print token in onCreate of my launcher activity. It takes time to refresh the token once you uninstall the app. Thats why you get null. You have to wait for a bit to get the token refreshed.
Sometimes network issues may occur, even internet is working fine....
check your FirebaseInstancId class defined in Manifest file in your android project.
I just fell in same issue and this is the way I fixed it. Call the block somewhere in onCreate() method of your activity. I want to sent the Token to my Parse server so you must change it based on your needs.
This is the sample I followed.
private void subscribeUserToParse() {
String deviceToken = FirebaseInstanceId.getInstance().getToken();
if (TextUtils.isEmpty(deviceToken)) {
Intent intent = new Intent(this, MyFirebaseInstanceIDService.class);
startService(intent);
return;
}
User user = UserUtil.retrieveUserFromDB(mRealm);
String objectId = user.getParseObjectId();
if (!TextUtils.isEmpty(objectId)) {
ParseUtils.subscribeWithUsersObjectId(objectId, deviceToken);
}
}
The biggest issue i faced after writing the correct code is:-
not the latest version of google play services in build-gradle,
So always use the google-play-services version shown by firebase in setting up dialog
I was having the same problem in a Android 4.2 device.
So, to solve the problem, first, check if your service, in the manifest, if it have a priority like this:
<service
android:name=".other.MyFirebaseInstanceIDService"
android:exported="false">
<intent-filter android:priority="1000">
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
Second, use a broadcast intent to notify your MainActivity that changed the token:
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
final Intent intent = new Intent("tokenReceiver");
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
intent.putExtra("token",refreshedToken);
broadcastManager.sendBroadcast(intent);
}
Then in MainActivity, in the onCreate, add this:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
LocalBroadcastManager.getInstance(this).registerReceiver(tokenReceiver,
new IntentFilter("tokenReceiver"));
}
and, in the MainActivity, add a new BroadcastReceiver:
BroadcastReceiver tokenReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String token = intent.getStringExtra("token");
if(token != null)
{
Log.e("firebase", String.valueOf(token));
// send token to your server
}
}
};
Since the getToken() method is deprecated, you have to replace it with a listener.
Just replace String token = FirebaseInstanceId.getInstance().getToken(); by the following code snippet:
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
#Override
public void onComplete(#NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
// Log and toast
String msg = getString(R.string.msg_token_fmt, token);
Log.d(TAG, msg);
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
You can find the official google documentation here: https://firebase.google.com/docs/cloud-messaging/android/client
And a nice medium post will help you out as well: https://medium.com/#cdmunoz/fcm-getinstance-gettoken-in-android-is-now-deprecated-how-to-fix-it-3922a94f4fa4
I have just migrated to FCM. I have added my class that extends from FirebaseInstanceIdService to receive a refreshedToken as and when appropriate.
My question is specific to the case when user installs my app first time and due to some reason, unable to receive a registration Id from onTokenRefresh. How are we supposed to handle this? Can I set a broadcast receiver from my FirebaseInstanceIdService class which will notify the Main activity when a registration Id is received?
if your device have no connection to the internet onTokenRefresh() is never called and you should notify to user his/her device has no internet connection
firebase has its own network change listener and when a device connected to the internet then try to get token and return it, at this time you can tell your main activity by sending a local broadcast receiver that registration token is received.
use below codes:
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d("FCN TOKEN GET", "Refreshed token: " + refreshedToken);
final Intent intent = new Intent("tokenReceiver");
// You can also include some extra data.
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
intent.putExtra("token",refreshedToken);
broadcastManager.sendBroadcast(intent);
}
in your main activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
LocalBroadcastManager.getInstance(this).registerReceiver(tokenReceiver,
new IntentFilter("tokenReceiver"));
}
BroadcastReceiver tokenReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String token = intent.getStringExtra("token");
if(token != null)
{
//send token to your server or what you want to do
}
}
};
}
Change this in manifest.xml file
tools:node="replace"
to
tools:node="merge".
As far as I know, token will be null only when you try to run your app on emulator on which google play service is not there and when you are using dual email id on you google play store(on you actual device), but only one email id is verified for the usage. Those are the cases which will give you null token and I have already implemented FCM in my new project. So for rest of any cases , token won't be null.
Use this class extends with..
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIIDService";
public static final String REGISTRATION_SUCCESS = "RegistrationSuccess";
#Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
Toast.makeText(MyFirebaseInstanceIDService.this,refreshedToken,Toast.LENGTH_LONG).show();
}
}
I was facing the same problem. I looked through a lot of SO posts and other forums and I found a solution that worked for me. FCM documentation says to use this method to get a token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
I found a post online (I apologize, I don't remember which one. I found it a while ago) that used this method instead:
String refreshedToken = FirebaseInstanceId.getInstance().getToken() (String authorizedEntity, String scope);
FCM documentation describes the first method as:
Return the master token for the default Firebase project.
While the second one is described as:
Returns a token that authorizes an Entity to perform an action on behalf of the application identified by Instance ID.
This is similar to an OAuth2 token except, it applies to the application instance instead of a user.
For example, to get a token that can be used to send messages to an application via FirebaseMessaging, set to the sender ID, and set to "FCM".
I have been looking into why the first method call takes a longer time to return a token, but I haven't found an answer yet. Hope this helps.
depending on your application logic you can write the code to handle the "new" token directly in the FirebaseInstanceIdService.onTokenRefresh() method, or you can use a LocalBroadcast to send this information to your activity if you need to change the UI when this event happens.
Note that when onTokenRefresh() is called your activity could be closed.
A possible implementation could a mix of the two options:
add some logic in onTokenRefresh() to send the token to your server
use a LocalBroadcastReceiver to inform your activity, if you have a piece of UI that need to change when the token is available.
If you are running it on your emulator, check that you have Google play services enabled in Tools -> Android -> SDK Manager -> SDK Tools -> Google play services
Once installed, reboot both Android Studio and your emulator
It worked for me
I encounter an issue with Android Firebase Auth using com.google.gms:google-services:3.0.0 and com.google.firebase:firebase-auth:9.0.1.
1 hour after authentication with Firebase (Google or Facebook), I get the following error:
W/PersistentConnection: pc_0 - Authentication failed: expired_token (Auth token is expired)
Why does Firebase token expire after 1 hour and how to extend this expiration period?
UPDATE
I still encounter this issue, Firebase token expires after 1 hour. Now I get the following message:
W/PersistentConnection: pc_0 - Authentication failed: invalid_token (Invalid claim 'kid' in auth header.)
I appreciate any help.
If we use default Auth providers like (Google, Facebook, Email..), updating "SHA-1 key" of your Application in firebase console would fix the token expiry issue.
In this discussion a Google developer shared a guide to solve this problem.
Guide: https://drive.google.com/file/d/0B94LePkXiqa6SXVFd3N1NzJHX1E/view
Try to implement FirebaseInstanceIdService to get refresh token.
Access the registration token:
You can access the token's value by extending FirebaseInstanceIdService. Make sure you have added the service to your manifest, then call getToken in the context of onTokenRefresh, and log the value as shown:
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// TODO: Implement this method to send any registration to your app's servers.
sendRegistrationToServer(refreshedToken);
}
The onTokenRefreshcallback fires whenever a new token is generated, so
calling getToken in its context ensures that you are accessing a
current, available registration token. FirebaseInstanceID.getToken()
returns null if the token has not yet been generated.
Code:
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIIDService";
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID token
* is initially generated so this is where you would retrieve the token.
*/
// [START refresh_token]
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// TODO: Implement this method to send any registration to your app's servers.
sendRegistrationToServer(refreshedToken);
}
// [END refresh_token]
/**
* Persist token to third-party servers.
*
* Modify this method to associate the user's FCM InstanceID token with any server-side account
* maintained by your application.
*
* #param token The new token.
*/
private void sendRegistrationToServer(String token) {
// Add custom implementation, as needed.
}
}
I hope its helps you.
The new maximum life time for Firebase Tokens is 1 hour - I read it in the docs earlier today.
As for Invalid claim 'kid' in auth header., I get exactly 2 search results on Google for that (: No documentation related to kid in Firebase docs. I guess we will have to wait for answers from Google (or switch back to the old version of Firebase if possible).
check if the last user is null or expired
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(context);
if (account == null || account.isExpired()) {
System.out.println("AccountGoogle: null");
GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(context, gso);
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
fragment.startActivityForResult(signInIntent, RC_SIGN_IN);
}