This is a general question around FCM tokens. Currently in my react-native app, as soon as I have new token, I make an API call to link this token with a user ID. And when this token is also refreshed, I make the same API call again.
We use those tokens to send 'Happy Birthday' push notifications and the likes.
I would like to know in the event that a user does not use the app, this token will get expired and we do not have a way to keep track of the token. The birthday push notifs will still be sent to the old expired token. How can we mitigate this? Any idea ?
I am actually looking for a way/strategy to still have the push notifications being delivered to the user even if they have not used the app for a while? Do you think scheduled push notifications might work?
When you send data to an expired token, you'll get a response indicating this. You can capture this response, and use that to remove the tokens from your database.
The samples repo for Cloud Functions has a great example of this. Modified from there:
var response = 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) {
// 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();
}
}
});
I would like to know in the event that a user does not use the app,
this token will get expired and we do not have a way to keep track of
the token. The birthday push notifs will still be sent to the old
expired token. How can we mitigate this? Any idea?
In my case, I register for firebase token each time my front end application gets launched. Most of the time it sends the registration token which is already stored in my database. However, when a new firebase registration token is received in my server side, I store the new token and while sending the push notification, I send the notification to all available registration token that I have.
You cannot figure out if the user has stopped using your application until you get notified about launching your application by sending the firebase registration token to the server side application. Hope that helps.
We shall drive campaigns that retain our clients. All the above answers are right though.
Related
I want to know if the user has allowed notifications on the current browser so I can show that state on the UI, but I don't want to trigger the request permission unless the user performs an action to enable notifications.
This is kind of a chicken egg problem because I don't have a reliable way to identify the current user browser.
I can store the FCM token on the firebase database and fetch it on the FE. But each token is unique for each browser and I can not compare it to anything, the only way I have to check if one of the existing tokens is for the current browser is to use the getToken method from firebase FCM and check the returned token is on the list of tokens, but that may trigger a request permission prompt and I don't want that unless the user asks for it.
One possible solution may involve storing a flag on local storage, but if the user clears it the UI will show notifications as disabled when the user is still subscribed to the FCM topic. I know the Notification API has a property that indicates if the user has granted notification permissions but that may have happened and the token not retrieved for some random network error, so I can not rely on this solely. Also it only indicates if the user has granted permissions, but on my application logic notifications may be disabled (because user choice) and that does not revokes the permissions.
Things are also not nice if user wants to disable notifications: in order to delete the token from the database, I need the current browser token, and the only reliable way to know the current browser token is to ask it with getToken which may trigger a request permission if the user has disabled notifications and I will never get the actual token and I need it to remove it from the database
Are there any guidelines about how to deal with this? The FCM docs are quite sparse
This is a solution that I'm not sure if it is super robust, but it's simple and probably to meet most cases.
First, check if the current browser has notifications allowed. If not, just show that value on the UI because the user will not get the notifications anyway even if they are subscribed on the backend side.
If notifications are already allowed you can safely run getToken (will not trigger permission request) and compare the value against the values on the user/profile database.
Because I'm using react here is an implementation on a hook:
const areNotificationsAllowed =
Notification && Notification.permission === "granted";
useEffect(() => {
// because we don't want to trigger a permission request
if (!areNotificationsAllowed) return;
// Notifications are allowed, we can safely run getToken to see if they are activated on this browser
messaging.getToken({ vapidKey }).then((token?: string) => {
if (!token) return setNotificationsEnabled(false);
if (notificationsCfg[token]) setNotificationsEnabled(true);
});
}, [areNotificationsAllowed])
I implemented Firebase Phone Auth for SignIn in my ReactNative project. Now I want to use this JWTToken to be passed to the API calls that I make to my server.
And at the server side, I would be validating the token which was passed through the API calls and respond with the proper response. My question is, how can I pass this token in the API calls that I make to my server?
I can store the token (within my first loading screen of the app, where it authenticates the User) in the localStorage and fetch it later in any of my screens to make the API calls
I can access the Token directly my importing the firebase package in each and every screen (from which am planning to do the API calls) like this: https://rnfirebase.io/reference/auth/idtokenresult and pass it in the API calls
But I was thinking about storing the Token (fetched during the loading screen) in a global variable inside my ReactNative project and that can be accessed from any screens. But I couldn't find how this can be done? Or which one would be the more appropriate way to do this?
EDIT:
This is how am getting the Token :
auth().onIdTokenChanged(function(user) {
if (user) {
user.getIdToken().then( token => {
console.log( token )
});
}
});
Storing the token in local storage is not going to work out well for you in the long run. ID tokens expire after 1 hour, and will not successfully verify on the server after that.
Each individual page should set up an ID token listener so it can use the most fresh token provided by the Firebase Auth SDK. The SDK will automatically refresh it and provide you with the latest token in the callback. Every time the token changes, you should use that value in your API calls. Use onIdTokenChanged():
firebase.auth().onIdTokenChanged(function(user) {
if (user) {
// User is signed in or token was refreshed.
}
});
I have a web application where users can sign in with Google.
To the sign-in process, I add a scope to be able to access Google Calendar.
Now that the user is signed in, I would like to - in server-side - get their current Google access token in order to make a request and get a list of their events.
Is there a way to get the current OAuth token (no need for refresh token) in order for me to make this completely on the server-side?
I'd say that you can check this article and put special attention to the recommendation for websites.
I understand you have configured already the consent screen, which is the first step of the basic steps on using OAuth 2.0. So I understand that you only have to perform the following steps:
Obtain an access token from the Google Authorization Server
Examine scopes of access granted by the user.
Send the access token to an API
I think you can also give a look to this other doc for more GCP insights over your goal to authorize the request using user tokens
Edited:
Regarding the Firebase Authentication, I understand this happens at the user's device, and you could use some code to retrieve the token and then send it to your back end servers as mentioned in here.
As a sample here there's the sample code for retrieving the token in Android:
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();
}
}
});
A little about OAuth 2.0
Whenever a user signs up to your app/website via Google or 3rd Party, an Authorization Code, this Authorization Code is exchanged for an AccessToken & RefreshToken.
The AccessToken sent via Google are valid generally for 60 minutes.
Offline Access (Server Side)
Let's break it down to two parts:
If your need to update within 60 minutes of user's last activity
You can use firebase along with gapi to achieve that. You'll be provided with the AccessToken that can be sent back to server to add to calendar.
More info on implementation
If you need to update after 60 minutes of user's last activity
Firebase & gapi's most method handle the AuthorizationCode flow internally. They even further refresh the AccessToken after 60 minutes. This is beneficial for most developers as they won't have a headache of managing all the tokens.
This method but, hides RefreshToken & AuthorizationCode from the developer. That is even if your server has the access token, it won't be able to refresh it and it would be deemed useless.
To achieve complete offline access, in the initial request to get AuthorizationCode you will need to send a HTTP GET parameter access_type to offline
GAPI provides you with grantOfflineAccess() method which returns the AuthorizationCode that can be later used on your server to fetch access token & refresh token.
Note: If you are storing AuthorizationCode in your database, make sure it is secure. The limitation in Firebase are set due to security reason. It is more secure to not talk with AuthorizationCode generally.
More links
https://developers.google.com/identity/protocols/oauth2/web-server
https://developers.google.com/identity/sign-in/web/reference
https://developers.google.com/identity/sign-in/web/server-side-flow
https://developers.google.com/identity/sign-in/web/backend-auth
Retrieve Google Access Token after authenticated using Firebase Authentication
I have read so many articles about firebase auth on web but couldn't find any clear explanation of how idTokens are supposed to be used on the client side. Here is what I know so far
After the user has logged in, we can get the token using the following method and it will automatically refresh the token if it has expired
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
We can then send this token to our backend where we can use firebase admin SDK to verify the id token and get the user uid.
admin.auth().verifyIdToken(idToken).then(function(decodedToken) {
var uid = decodedToken.uid;
// ...
}).catch(function(error) {
// Handle error
});
Here are the things which I don't understand.
Do I need to call getIdToken() method before each API call to the server to get the idToken?
Firebase documentation says that the token expires after 1 hour. So am I supposed to keep a track of that using localStorage and then reuse the token for 1 hour till it expires and then issue a new one using getIdToken()?
Should I instead create a session on the backend with the uid which won't expire and then use that to verify if the user has logged in or not?
No; as you noted, the token is valid for an hour. You can reuse the same token during that period unless you have a reason to refresh it (for example, if you add custom claims)
Ideally your server will return a 401 Unauthorized or something when the token is invalid. Most REST libraries provide the ability to add interceptors in the request chain, so you can check if you get back a 401 code and only refresh the token when necessary.
There is no need for a backend session unless your business logic requires it. The Firebase library will handle persistence for you.
I'm trying to implement the Authorization Code Flow to link actions on google with my firebase users:
https://developers.google.com/actions/identity/oauth2-code-flow
So far I've understood the flow as follows:
1 - User access the application and is redirected to the authorization url endpoint
2 - User signs in and google receives an authorization token
3 - Google sends this authorization token to the token url endpoint and gets an access token a refresh_token and a expiration time
4 - Google sends the refresh token to get a new access token when the access token is going to expire and gets a new acess token and a new expiration time
Did I get everything right?
As authorization token and access token I'm using the custom tokens from Firebase. My question is, how can I implement the refresh token? I cannot get this token from the Firebase Node.js SDK server side.
How can I greate long-lived tokens with Firebase Node.js SDK?
Is there another approach?
Yes, you got the OAuth2 process right.
The Firebase Admin SDK lets you sign the user in to your Firebase service using generated custom tokens. Though the custom token expires within 1 hour, once user is signed-in, they should be authenticated indefinitely (i.e. until user signs out). As such, there is really no need for SDK to generate refresh token.
I'd suggest a different approach. Use Actions on Google's SignIn helper intent to get user's info, such as email, name etc. Using this info, you will be able to sign the user in to Firebase as follows (referenced from the "Create Custom Token" Firebase doc):
var uid = "some-uid";
admin.auth().createCustomToken(uid)
// token == custom token
.then(function(token) {
firebase.auth().signInWithCustomToken(token).catch(function(error)
{
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
})
.catch(function(error) {
console.log("Error creating custom token:", error);
});
References:
"How to use refresh token?" from Firebase's GitHub
"Create custom tokens" from Firebase's docs
"Request Signin helper" from Actions on Google docs