How to handle multiple Firebase FCM tokens per user? - firebase

From the official documentation I understand that the way it works is something like this:
User installs app, FCM token is generated
Sending token to app server
Server uses token to send push-notifications to this device.
What if at the same time this user installs app on the other device - should I store multiple tokens per user on the app server? If yes - that means there should be something like checking for which ones are expired?

I also came across the exact challenge and had to resolve to a solution:
Storing each token for the user against the device id.
It's interesting enough to know that this function in fact exists in the firebase messaging method. But more surprising is the fact that there's no documentation to handle such scenario.
https://firebase.google.com/docs/reference/android/com/google/firebase/iid/FirebaseInstanceId.html#getId()
Summarily, while sending the new token to the server, also send along the device id returned by the getId() method and use it to enforce uniqueness of token per device.
And to also apply this solution while taking advantage of device grouping feature of FCM, you can make a server request on the FCM group, in order to delete the old token on that device-id before replacing it with the new.

What if at the same time this user installs app on the other device - should I store multiple tokens per user on the app server?
Yes. A user could have multiple devices, a case where Device Groups are commonly used.
If yes - that means there should be something like checking for which ones are expired?
If a token expires, a callback is triggered (onTokenRefresh() for Android), from where you'll have to send the new token to your App Server and delete the old one corresponding to the user/device.

Graykos, You can do sth like this:
Each time a user have a new login, get a new token from google, and when logout delete that token (Edit note: Close app & run it again, not called a new login and it's not create a new token).
So if user login from multiple device/browser OR multiple user login from one device/browser one after the other, you can nicely handle all of that.
in this way "multiple user login from one device/browser one after the other", all of them have the same token (so delete and renew for each login)
As Andres SK mentioned in the first comment of your question, you can delete the token when failure happen (maybe the lost device that user cannot logout from).

I came across the same problem, How I tried to solve this is to maintain a data structure like this. And save this data to my notifier server's database.
type UserToken struct {
UserId string
Tokens []DeviceToken
}
type DeviceToken struct {
DeviceName string
Token string
}
Whenever you get a new device token from firebase just replace the existing one with new one for that device.
And for the web part I think storing the last one is enough.

I have a similar situation and found out the following error response code when trying to send to an expired token:
{
"error": {
"code": 404,
"message": "Requested entity was not found.",
"status": "NOT_FOUND",
"details": [
{
"#type": "type.googleapis.com/google.firebase.fcm.v1.FcmError",
"errorCode": "UNREGISTERED"
}
]
}}
My app server accepts multiple tokens per user assuming they use multiple devices. When sending a new message, I will try to send it to all tokens related to the user. Those that return this error will be deleted so future message will only be sent to active tokens.

Related

Is it possible that the ID token generated by FCM is a duplicate?

According to the documentation, we know that the client can send its ID Token, or Registration Token, to the application server to notify the client that it is logged in.
https://firebase.google.com/docs/auth/admin/verify-id-tokens
I would like to know if this token can have duplicate values? I know it is similar to a JWT, which contains information such as uid, issue time, etc. If the same account logs in at the same point in time, is it possible for different devices to generate ID Token with the same value?
No it is not possible. Application installation is one of the things that change.
Assuming you mean the FCM token, used to send e.g notifications.

Synchronize users created with Firebase Auth to my custom backend

I want to use Firebase Auth for my user login/registration process. Everything else should be handled by my own backend (spring boot app + postgres db).
Now I'm asking myself how I can synchronize a new created user to my user table in postgres. I thought about the following:
REST call through client - Everytime I get a success event from the firebase sdk I call an additional request to my backend which sends uid, username etc.
Problem: What if my backend call fails but the register process was successful ? That would lead to an inconsistent state since (at least thats what I understanded) I can't easily rollback. That would lead to situations where a user can login into my app without my backend knowing the user. This would crash/ invalidate all my following queries (e.g. search after user xyz would lead to no result even though he/she exists)
Check the existence of the user in the postgres database
Here I would query the uid from the database (which I got from the jwt) and create a new user if it doesn't exists in every incoming request.
Problem: The user query is a unnessecary overhead for every incoming request.
Trigger with cloud functions - When I understood it right firebase auth is firing events when a new user is created in cloud functions. This could be used to make the external api call.
Problem: I dont know what happens when my external rest call fails at this point. Can I rollback the registration ? Will I be ever catch this event again ? I also proably would have an eventual consistency situation, since I dont know when the cloud function triggers. Furthermore I would prefer not to include cloud functions to my stack
Is there any way how I could do this in a transactional manner ? Did anyone else tried is using sth simular ?
Thanks for every help!
The easiest way is actually to not synchronize auth data, but instead decode and verify the ID token of the user in your backend code.
This operation is (by design) stateless, although Firebase's own backend services often implement a cache of recently decoded tokens to speed up future calls with the same ID token.
Apparently, I finally came up with a different solution:
Register user per Firebase SDK (e.g. with email + pw method)
Make a post-call to my own registration api including the resulting uid from the previous step and some metadata
API creates a new user including a column with the UID + Fetches the firebase token of the user and adds an internal claim that references to the internal Postgres UUID via Admin SDK.
Frontend gets the created user and hard refreshes (very important, since the previously fetched token won't contain the newly added claim !) the firebase token and verifies that it contains the token. If it does -> everything is cool, if not some oopsie happened :) That will require a request retry.
Later when you start your app you can just check if the passed token contains the custom claim, if not open the sign up/sign in page.
Every endpoint except the one for registration should check if the claim is set. If not just forbid the request.
How to set custom claims:
https://firebase.google.com/docs/auth/admin/custom-claims#set_and_validate_custom_user_claims_via_the_admin_sdk
You can use the Firebase Admin SDK to create the user account from your back-end instead of from the client.
So first you create the user in your database, then grab the ID and use it to create a user with the same ID in Firebase.
If all goes well, send a confirmation to the client and sign it in using the same credentials they entered.
Why not creating an endpoint in your backend service and call this endpoint when a client side authentication succeeds?
This method should do 2 things:
decode token to get access to Firebase user object (Firebase Admin)
Compare Firebase user with your internal user table. if it doesn't exist you can create it using firebase user object, otherwise do nothing.
This solution allows you to do other nice things as well (Syncing user info between Firebase and your internal db, providing a way to let a frontend know if this user is new or not, ...) at a relative small cost (1 get call per sign in)

FCM Token - When should I store/save it on my DB?

I am not sure what a proper FCM token handling mechanism would be so I’m writing our process down here just to get some validation or suggestions for improvements:
Fetch FCM token on client Login (Flutter)
Save FCM token on our Database (Using our REST API)
Delete FCM token on Logout (Using our REST API)
Q1: Should we be getting the FCM token more often than just on login? AFAIK, FCM token only changes on app re-installs, clearing cache, etc. Does this also include app-updates from the PlayStore? In that case, should we save the FCM token on every app launch since the user will remain logged in after an app update and hence we wouldn't trigger the save FCM call.
Q2: Did I mention the right way to handle deleting FCM tokens from our DB? We don’t want the user to keep getting notifications once they have logged out.
Q3: An add-on idea is to send the device_id to the server along with the fcm_token so that server deletes all previously saved FCM tokens for that device_id. This is useful to not have useless tokens on the DB from cases where the user uninstalls the app without logging out (which means that the DELETE fcm_token call never went through.)
The FCM token is refreshed under conditions that you don't control, and those conditions have even changed over time. To handle token updates properly, you'll need to implement both initially getting the token and then monitoring for token updates.
Note that FCM tokens are not associated with a user. It is fine if you want to associate them with a user, but it's up to your application code in that case to maintain the association. So that for example includes deleting the token from your database when the user signs out, as you're doing in step 3. 👍
For keeping your token registry clean, you can indeed do this proactively as you intend, or reactively as shown here: https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js#L76-L88
Hi Rohan fundamentaly you should use below logic to save tokens on server.
Step1:
as soon as you get token in callback whether new or same try to save it localstorage.
Step2:
Call your REST API to save it to your server. it is upto you if you want to send unique user identifier along with the token.
Step3:
It is obvious you will recieve token callback a lot of time so you can check whether you have similar token in localstorage, it means you have the token on the server so no point calling REST API.
Step 4: Now your app can send events back to server and based on it trigger Push notifications to the users.
Step 5: You can Add/update user token based on uniqye user identifier. In some cases a user can be guest user, so your app should generate guest userId and link it with token.
Stay safe.

How a Firebase token is generated?

I'm doing analysis on Firebase Token and understood below points:-
-> A Firebase token is saved in database which will be used for sending notifications.
-> The token generally do not expire except in the following cases:
- The app deletes Instance ID
- The app is restored on a new device
- The user uninstalls/reinstall the app
- The user clears app data.
-> When we use a token which is expired we get errors like Not Registered from the response while sending messages.
-> To avoid the error, we should be deleting the token from database.
However I have found that If we login to a cloud application (which is my app currently), a new fcm token gets generated when i logged in to a new browser say FireFox, Edge etc.
So, the token is generated based on browser or System IP or what exactly the Fcm uses to generate a token ?
The method used to generate the token is an implementation detail, and you should not depend on that to build your app.
A token uniquely identifies a device. Each device receives messages independently of each other, and does not know anything about the user of that device. It's expected that if a user signed into an app on multiple devices, that each device would generate a unique token. If you want to send message to a user, you will have to map each of the user's device tokens in your own database, and send the message to each of them, or only the ones that the user chooses.
You can expect that device tokens might change over time. If you send a message to a device, and the API tells you that the token is not valid, you should simply delete it from your records.

Best practices for push notifications in multi user applications?

I'm working on a push architecture that needs to support applications which allow for multiple users. This means more than one user can log into the application with their credentials. The problem I'm running into is what if user A allows push notifications, then logs out, then user B logs in and starts getting user A's push notifications?
What are some best practices for handling this type of thing? One thought I had was you could remember the last user who logged in and only display push notifications to the "logged in" user. You would have to send some sort of user context in the message payload so it could be checked against the logged in user. However this feels a little funky.
Anyone else ran into this? It's seems like a really relevant problem, especially for tablets where families tend to share the device.
We're implementing this by Registering the device with APSN, getting the device token and sending this to our server through a ws.
On the server side the device token is only associated with the last logged in user.
New app
User A (first ever user) uses IPAD A
Register with APSN, get token
Send token to our servers through ws
Search for token in db, token is new, store it
assign token to USER A
Next user logs into app
Register with APSN, get token
Send token to our servers through ws
Search for token in db, token exists already
Remove connection to USER A
assign token to USER B
SEND Notification to device WITH USERNAME
if username is logged in show it - else dont
Still not perfect as its sent to home screen first so to ALL users
I think your suggestion is acceptable in a multi-user app. It is much simpler to implement this in the client side, than on the server side. The downside is extra bandwidth wasted to send an unneeded notification. But vast majority of the usage is probably single-user so this may not matter much.
The alternative is to track the logged on users on your server and their current reg_ids. This could be more complicated because A could be logged on on multiple devices, then logs out from device 1, and B logs onto device 1, etc. and your server has to track all of these. So probably another table to track the relationships between 'Logged On Users' to 'Reg Ids'.
If you hate the idea of sending unneeded notifications, go with the server route. If you value Keep-It-Simple principle, go with the client route.
Let's suppose users of your app can logging on multi devices.
We have to make two API on server side:
func setUserDeviceNotifyToken(userId: Int, deviceToken: String) {}
func removeUserDeviceNotifyToken(userId: Int, deviceToken: String {}
On your app side, you have to call setUserDeviceNotifyToken API on every Login In and call removeUserDeviceNotifyToken on every logout.
On server side, you can track every user with its deviceNotificationToken and send notification for correct device.
Notice: If your service doesn't suppose to support multi device login with one user, you can handle it just by one updateUserDeviceNotifyToken and pass null for remove user's device token.
Notice 2: Do not let user logout before calling removeUserDeviceNotifyToken API.

Resources