I am using Cloud Firestore to keep app tokens to send push notifications. However, when the user uninstalls and reinstalls the app, Firestore receives a different token for the same user. How can I delete the relevant row of the previous token when the user uninstalls the app?
Thanks in advance.
Usually you'll want to detect when a token becomes invalid, and remove it at that time. E.g. when a token gets cycled (which happens every few weeks while the user has the app installed), you'll want to use that moment to remove the old token from your database and add the new one. Doing so minimizes the number of outdated tokens you have in your database.
So in steps that means in onTokenRefresh():
Check if there is a token in local storage (e.g. shared preferences). If so, remove that token from both the database and local storage.
Store the new token in both the database and local storage.
But in your case that is not possible, since onTokenRefresh won't be called when the app is uninstalled, and you won't have knowledge of the previous token when it gets reinstalled.
The easiest way to deal with outdated tokens left behind in this and other ways is to delete them when sending to that token fails. The sample of sending FCM notifications using Cloud Functions has a good example of that:
admin
.messaging()
.sendToDevice(tokens, payload)
.then((response) => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensSnapshot.ref.child(tokens[index]).remove();
}
}
});
});
It's quite simple, when the user reinstalls the app and logs in again, just override the old token with the new one. If you have also other stuff that needs to be deleted once the new token is generated, just check the existing token with the new one. If the tokens are different, you may delete all the unnecessary stuff.
Related
I have an app where a user can register/login via Firebase. A user can have multiple devices among which all his data is being shared (of course he must be logged in). I keep track of all devices by their firebase device token and send appropriate update notifications when the user updates something on a particular device
Now I know that the firebase token is being refreshed, but how do I know that a token is invalid? Lets say a user has 4 devices where he is logged with one account. Now he delete the app on one of them, and installs it again, so he gets a new token. This means that now I have 5 device tokens on my server but still just 4 devices. The best approach would be to tie a token to some non-changeable device id like MAC oder IMEI but because of privacy policies that is not possible.
Is there some other way to fish out the tokens that have been revoked/invalidated?
The common way to detect expired/revoked FCM tokens is during sending of messages. FCM will at that point tell you exactly which tokens have expired, and you can then remove them from your database.
For an example of this, see this Node.js code from the functions-samples repo:
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
const response = await admin.messaging().sendToDevice(tokens, payload);
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
I have just started learning react-native and thinking of integrating firebase to it. Now consider my question scenario:
There are two users A & Bwho have the react app running in their device( none of them are admin). Now I have studied that when we connect our react native app to firebase, every instance of the app running on a device gets a unique token and that token is stored in firebase itself.
Now suppose user A wants to send a " notification or message" to user B. Now see the below code I saw on firebase official website:
// This registration token comes from the client FCM SDKs.
var registrationToken = 'YOUR_REGISTRATION_TOKEN';
var message = {
data: {
score: '850',
time: '2:45'
},
token: registrationToken
};
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
This method seems quite straightforward, but is there really any method using which user A can know the unique token of user B like this:
const token= firebase.getToken('B');
And then use this token in the above code to send notification to user B. Is it poosible to do it using firebase?
Thank You.
The code you found uses the Firebase Admin SDK to send messages. This SDK grants its users full administrative access to the Firebase project, so can only be used in trusted environments, such as your development machine, a server you control, or Cloud Functions. It cannot be used in the app you send to your users.
You will need a trusted environment to send the messages to the users. For more on this, see:
The Firebase documentation on FCM architecture, which has this handy diagram
How to send one to one message using Firebase Messaging
How to send Device to device notification by using FCM without using XMPP or any other script.?
I would like to add push notifications to my react native App but I would need some clarifications as there are plenty of different informations and implementations all over the web on that subject.
Currently my App is communicating with a node.js backend. So I have used the Firebase Web SDK in order to send push notifications to app clients.
Now it's time to register the app clients to Firebase Cloud Messaging in order to get tokens and save them with user related data. Then to be able to send targeted notifications to specific users (node.js backend role).
I've read to use the react native firebase library and more especially the messaging module to accomplish that.
Following that Device registration token tutorial, it's pretty clear. But where is the best place to request a token ? I assume that it should be when the user logs in to the App but we are not sure to get the token on time...
With that code we retrieve an existing token:
firebase.messaging().getToken()
.then(fcmToken => {
if (fcmToken) {
// user has a device token
} else {
// user doesn't have a device token yet
}
});
Tell me if I'm right:
I understand that if the app instance has already a token, I'll get it and it's time to associated it with user related data in database. But if it doesn't, I need to listen for a new token using that :
componentDidMount() {
this.onTokenRefreshListener =
firebase.messaging().onTokenRefresh(fcmToken => {
// Process your token as required
});
}
componentWillUnmount() {
this.onTokenRefreshListener();
}
But where should I start listening for it ? On the login component ? If the user log in before getting the token and navigate to another component, so the login component will be unmounted. It will be still listening ? And then I need to update the user data with the token ?
Or simply when the user log in, I need to wait till getting the token before navigate to another component ?
I am making an android chat application using firebase. So far I have made a common group chat app. Now I want to make it an individual chat app, which will have a Users list and when we select a person from that list we can chat individually with him/her. However I am not able to get this Users list from Firebase. I have kept Google Sign in and Email sign in options in the Firebase Auth UI. Any help would be appreciated
If you need to lookup users by uid, email or phoneNumber, you can use the Admin SDK to do so:
https://firebase.google.com/docs/auth/admin/manage-users
You also even have the ability to download all your users:
https://firebase.google.com/docs/auth/admin/manage-users#list_all_users
You would need to do that from a Node.js backend server or via HTTP endpoints with Firebase Functions.
In addition the Admin SDK allows you to set custom user attributes which could be helpful if you want to create different user groups:
https://firebase.google.com/docs/auth/admin/custom-claims
admin.auth().setCustomUserClaims(uid, {groupId: '1234'})
The Firebase Admin SDK allows retrieving the entire list of users in batches:
function listAllUsers(nextPageToken) {
// List batch of users, 1000 at a time.
admin.auth().listUsers(1000, nextPageToken)
.then(function(listUsersResult) {
listUsersResult.users.forEach(function(userRecord) {
console.log('user', userRecord.toJSON());
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
})
.catch(function(error) {
console.log('Error listing users:', error);
});
}
// Start listing users from the beginning, 1000 at a time.
listAllUsers();
Note: This API is currently only available for the Admin Node.js SDK.
via https://firebase.google.com/docs/auth/admin/manage-users
As pointed out by #Sam earlier, you can fetch details from Firebase DB. So every time a user signs up, add his FirebaseUser details (preferably his UID, under which you store his other details if required) to the DB. Then simply put a listener on the Database in your next activity, and you can fetch a list of all registered users.
const allUsers: firebase.auth.UserRecord[] = [];
const listAllUsers = async (nextPageToken?: string) => {
const res = await firebase.auth().listUsers(1000, nextPageToken);
allUsers.push(...res.users);
if (res.pageToken) {
await listAllUsers(res.pageToken);
}
};
await listAllUsers();
console.log(allUsers)
You cannot retrieve the data of all authenticated user from Firebase Authentication, however you can only get the current user.
In Order to get the data of registered user you need to store it into database and then retrieve the whole array, or you can just keep the authentication flag which can be set or reset if the user is registered in your all-user table and vice versa.
As mentioned by #Jason you can try Admin SDK as it is mentioned in the documentation that listAllUsers() can retrieve batch of user data.
The detailed explanation can be found IN THIS THREAD.
If you want to view a list of users that has registered thru Firebase Auth, you may view them in https://console.firebase.google.com/ then go to your project and select authentication, in the Users list is all the users that have registered thru Firebase Auth.
I am trying to implement Firebase cloud messaging in my Android app through a Node.js server and I have got stuck at a usecase.
I saw the Firebase tutorial of creating a device group using registration tokens to send messages/notifications to all devices with the same user logged in, what I don't understand is what happens when one of the registration tokens get refreshed by onTokenRefresh() method.
How will I distinguish which token to change as all will be belonging to the same user?
Update:
Ok, so now I have got stucked on another blocking use case. I am creating a user group identified by the user id from my server. If user uninstalls and reinstalls the app immediately and another user logs in on the device, if I call a gcm message on the previous user group this device still receives it.
Is there any way for the gcm to identify is the device it is sending the notification to is logged in or not and if it is, is it logged in with the same user as for the group?
There is another way to solve this problem using Cloud Firebase Functions.
How will I distinguish which token to change as all will be belonging
to the same user?
Using Firebase Functions, you don't have to. Within onTokenRefresh(), you send the new token to the server.
For Example:
The user has 3 devices, each of which have a token that has been sent to server.
*** deviceTokenA/B/C represent UIDs of the token ... we do not know what they are, or which device they belong to.
UserId:
Device Tokens:
deviceTokenA: true,
deviceTokenB: true,
deviceTokenC: true,
Now, the User is on the device that triggered deviceTokenA. The token is refreshed, and onTokenRefresh() is called, sending the token to that collection.
onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
sendTokenToServer(refreshedToken);
}
sendTokenToServer(String refreshedToken) {
// send to Firebase or Firestore Database, put in the Device_Tokens collection. }
Now, you will have 4 tokens in the collection.
UserId:
Device Tokens:
deviceTokenA: true, // this one has been "unregistered"
deviceTokenB: true,
deviceTokenC: true,
deviceTokenD: true, // this one has been registered.
The deviceTokenA no longer applies, because it was refreshed, and is not attached to an actual device anymore.
When looking at the device Tokens, we still don't know which ones are good, which are bad, and which tokens belong to which device. That's ok!
So, then create a forEach loop, getting each Token, and then send an FCM to each of these Tokens, FCM can let us know which tokens were sent successfully. One of them will return an error. If it returns an error saying the token was bad, we can then catch the error and delete that token, so it will not be called again.
// outside for Each loop
var promiseHolder = [];
// create a forEach loop, iterating through the collection of deviceTokens
// within that loop, put:
let innerPromise = admin.messaging().send(message)
.then(response => {
console.log('notification sent success: ' + response);
})
.catch((error) => {
console.log('Error sending notification: ' + error);
// if the error == bad token message, then Delete the Token.
if (error == 'Error: Requested entity was not found.') {
console.log('you matched the error, token doesn't work, handle here.');
//delete the old token
return admin.firestore()doc(`users/${userID}/device_tokens/${token_id}`).delete();
}
}
// still within forEach loop
promiseHolder.push(innerPromise);
// end the forEach Loop, and outside forEachLoop put:
return Promise.all(promiseHolder);
So I've been thinking about how to go with this scenario. First off, let's put in the instances when onRefreshToken() is called:
This will not be called very frequently, it is needed for key rotation and to handle Instance ID changes due to:
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
Guess with that, you can say that 'onTokenRefresh()` will be called after one the above happens and if the device is online (of course it has to be online on order to get a new token). So I guess here's how I'd go on the scenario:
First off, upon registration, I would save the registration token and pair it along another identifier, let's say a deviceId (since we're in a scenario for a user with multiple devices) in my App Server.
So assume I add in 3 registration tokens, those are also paired with their deviceIds. I add them all to a device group.
Now say one of the devices triggers the onTokenRefresh(), I would immediately send a delete request to my App Server for the registration token that is currently paired to that deviceId (you should also delete it in any device group(s) it's connected to), replacing it with the new one, then re-add it to the corresponding device group(s).
That's the simplest way I can think of. The key here is for you to pair the registration tokens with another identifier and use it to find which registration token you need to replace.
at moment i use this method. in my database i create a node with devices id
deviceId: {
uid1: deviceId,
uid2: deviceId,
uid3: deviceId
}
another node with the users that are subscribed to receive a notifications
newsSubscriber: {
uid1: [array of subscribers],
uid2: [array of subscribers],
uid3: [array of subscribers]
}
when i can send a notification by my nodejs script i get all users that are saved in the newsSubscriber node, and for any user i get his deviceId and i put it into an array of devices to send a notification.
There are only one problem, i read now that in this mode there are a limit of only 20 devices!.
but i think that this is a good easy method to have a corresponding deviceId for any user because when i use the logout or the login in my app i can change the correspondent deviceId for any user, so as to have consistency between user and device.
what happen if to bypass the limit of 20 devices i send the same notification to different groups of 20 devices?