Updating Remote Configuration data when a user property changes - firebase

I use Firebase Remote configuration to control my app features per user segment and so I apply conditions on remote configuration parameter based on some user properties.
The issue is that some of these user properties could be changed when the app user make some actions that change his/her segment (like user was free user and becomes a paid users) and in this case, Remote configuration doesn't fetch the new values so the user sees wrong data in the current app session (sees data related to his previous segment not the new one) and will not see the right data until he/she closes the app and opens it again.
-Are there any solution to force Remote Configuration to update the cached value?
-Is it right to use Remote Configuration in these case or we have to move to another solution like RTDB?

You can trigger Firebase reload by sending silent FCM message either to devices with specific property value or to all devices. Naturally you need to implement handling for the notification and reload triggering. This page describes the implementation very well.

Related

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)

Is it really necessary to update the subscription on every page load?

I'm currently working on a web app which will allow a user to subscribe to push notifications. We'll store the subscriptions in a database table mapped against the user's ID and when a notification needs to be sent, we'll look up the user's subscriptions and send the notifications.
I've been following this guide:
https://developers.google.com/web/fundamentals/codelabs/push-notifications/
All is going well, but something just doesn't feel "right".
On every page load, the service worker is registered, then it checks if they're already subscribed, then even if they are it calls updateSubscriptionOnServer() which contains the following comment:
// TODO: Send subscription to application server
This effectively means that every page load is going to be attempting to write the same subscription back to the database. Obviously we'll handle that in the application, but it doesn't seem ideal.
With the Web Push API, is this the expected approach?
Many thanks
Yes it can be useful to send the subscription to server on every page load:
The user may have changed permission from blocked to granted (or default) in the browser settings, so you want to create the subscription and send it to the server
The subscription endpoint may change for different reasons, so you need to make sure that the current endpoint is sent to the server (the previous endpoint will return 410 Gone)
A compromise between the points above and performance can be to send the subscription to the server only on given pages (e.g. homepage).

About Firebase auth returned object

I have a react native app that users can login or signup through it, I use firebase to log them in but I don't understand what am I supposed to do with the returned object from firebase.auth().signInWithEmailAndPassword(email, pass); and createUserWithEmailAndPassword(email, pass);.
Am I supposed to use one of the parameters it returns? how?
Is each backend call (my backend not firebase's) supposed to be with one of the strings it returns?
Also which strings should I save on local storage so the users won't have to login again? I set firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL);.
Also which strings should I save on local storage so the users won't have to login again? I set firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL);
This ensures that Firebase persists the authentication token in local storage. You don't need to do anything else. When the app restarts, Firebase automatically finds the token in local storage and re-authenticates the user with that information.
Most likely you will need to add a listener to onAuthStateChanged() to ensure you can update the UI of your app to this authentication state. For more on this see getting the current user in the Firebase documentation.
In the case of a SPA (Single Page application), the returned object from firebase.auth().signInWithEmailAndPassword(email, pass); should be used to set the user name, email fields and display photographs inside your protected app pages. Also, since the user is in signed in state you can display private links inside this promise. You can also update user's profile inside this promise.
In the case of a multi page application, you might check the profile verification status and then redirect to your app's home page on the basis of the same.
You are supposed to get the ID token in your backend to identify valid requests
Firebase automatically stores the current user data in local storage which persists till the user logs out or the localStorage gets corrupted(?). You can confirm this from the fact that firebase auth does not work in case of Safari private browsing mode as it doesn't support localStorage methods.
In short, nothing has to be done on your part to ensure data persists in localStorage, Firebase uses onAuthStaeChanged event listener to toggle sign in stage for a given user across all registered devices.

Google OAuth Always Showing Consent Screen

I'm building an installed application that will have features requiring the Google Drive REST API using Qt and C++. I understand Qt is now releasing new libraries to support OAuth flows but let's assume I'm a student and learning to use OAuth at this layer is a requirement for this project.
In my application, I have a working OAuth flow for installed applications that ends with an Access Token and Refresh Token being stored using QSettings (I'm open to input on whether this is a disastrously bad idea too). The application requires no authentication/login for its own sake/data, but it does need authentication to Google for calling API's using an Access Token. This application has no associated web backend being hosted; its simple and should be deployable completely locally (I've written and included a simple TCP server that will receive the authorization redirect_uri and will run and close when called from within the application).
As such, I'm curious about the best way to make sure that, when a user opens my application and wants to use the Google Drive features, they are appropriately authenticated on Google's end. Say, if I maintain an access token in the registry, and this access token is granted per-user/per-application basis (right?), then how can I make sure only the user the token belongs to is able to make calls to the API with it?
Here's my understanding and approach; feel free to correct me or educate me if I've got the wrong interpretation.
If an Access Token is found, perform the following:
Open a browser page to a Google login domain and have the user authenticate there (this could prohibit a user from being able to use a cached login session that would have access to a token they otherwise shouldn't have access to)
If user has correctly authenticated with a Google account, return control to the application and make a test call to an API using the stored token.
If the call fails (responds with an invalid_credentials) I should be able to be sure its because the access token has expired and the application will go through the flow to renew an Access Token from a Refresh Token.
If no Access Token is initially found:
Start a normal OAuth installed application flow
Get the tokens and store them so that when the user opens the application next time the former procedure is used
My issue then is the first two steps if the Access Token is found. Nominally this could be done by the typical OAuth flow but it appears that when using a localhost as the redirect uri, Google will always prompt for consent, regardless of settings for prompt and access_type authorization query parameters.
What can be done to accomplish these first two steps in a way that my application can control (i.e. not a solution that relies on a backend server being hosted somewhere)?
If this question is too open-ended for SO requirements I can make some more restrictions/assumptions to limit the problem domain but I'd rather not do that yet in case I unknowingly rope off a good viable solution.
Thanks for reading! Sorry if its a verbose; I wanted to ensure my problem domain was fully fleshed out!
If you are using an installed application, I wouldn't recommend using or storing refresh tokens. Storing refresh tokens on the client side means that if an intruder gains access to the client's application, they have infinite access to the user's application without ever having to enter the user's credentials. If you do insist on having a refresh token, ensure you follow the Google's installed app flow, with the code_verifier parameter included in your requests.
If the access token is found, you should try to verify it, and if verified then use it at the google api, otherwise force the user to login again (or refresh it if you chose to still use refresh tokens).
If no access token is found, your flow sounds fine.
A few notes on loggin in with Google:
Google will only return a refresh token if you specify access_type=offline in your auth request.
Google will only return a refresh token on the user's first authorization request, unless you always specify prompt=consent in your query params.
In my experience, when leaving out the prompt query param, the user is not prompted for their consent again. If they are logged in to google, you will get a new access token, but no refresh token, unless you have prompt=consent.
I think the idea is you use prompt=consent if you have no record of the user ever using your application. Otherwise if they have used it before, you may prefer to use prompt=select_account to allow the user to select which account he wants to use in case he has more then one, or you can just use prompt=none.
This is just my understanding of it all.
My approach I ended up using was just to deploy with an SQLite db that will be stored in the AppData roaming directory. The db schema includes a field for the user's Name (from the OpenID IDToken field if it exists), the user's picture URL (again from IDToken if it exists), the refresh and access token strings (will be stored as encrypted strings when I get around to it), the user's UID/sub string, and a field for a user name and password.
These latter two fields are authentication fields for within my own application, which, again, I wanted to avoid but it seems impossible to do so. So the user will be prompted to enter a username and password into a form, and these credentials will be checked against the existing SQLite db file mentioned previously.
If they exist and are correct, the user gets logged in and will have access to their respective access and refresh token.
If the user has forgotten their password, they'll be asked for reconsent (going through the installed app flow again), and whatever password they provided during initial login will be used as the reset password. It is considered, for my purposes, that logging into Google for the installed app flow is proof enough that the user account belongs to them and they should have authorization to reset the password.
If the user is a new user and doesn't have a record in the local SQLite db file, then they can also click a button to "Create New Account" - which effectively goes through the authorization flow as well but this time a whole new record is posted to the SQLite db with the appropriate fields filled.
There's still more optimization that could be done but at least I am getting closer to the level of security and control of access to Google user accounts that I want.
I'm not marking this as an answer because I feel like this solution is still not desired and that there should be an easier way. So if someone has evidence or experience of providing an equivalent level of authentication control without needing to maintain a local user account database then I would be more than happy to mark such a method as the solution!
Thanks again!

Firebase References - Local Authentication

When I create a firebase reference and take a snapshot of that reference while authenticated through firebase authentication then I log out, that data remains.
Requirements:
Page cannot be reloaded or force reloaded after logout
Cannot Clear Firebase Session or all data will need to be reloaded
Must clear only the data that was accessable to the user and not to the public
Must also reload data that is owned or is in the same group as the non-escalated or guest user after the other user has logged out.
I am considering creating objects that wrap my references that will supply a user group and other permissions property as is commonly seen on linux. This will allow me to use the current user object that provides group and user details as a base for deactivating or clearing local data.
Is there a standard way of doing this?
Am I making more work of this than is needed?
I will be hardcoding the knowledge of what groups have permission on the clientside to be replaced later with metadata provided by firebase. I plan to eventually keep track of all references available to a specific user under the user id as well as keep track of the references available publicly and as well again keep track of references available to specific groups where the highest permissions takes priority.
Step one I am taking is creating a way to manage the references clientside and build in checks for authentication change and then utilizing the user details provided in the current auth apply needed changes with authenitication change kicks off. Is it easier to just initiate another call to the server for that snapshot, will that snapshot change if it is no longer authorized?
You control your own app and the prefs based snapshot (or wherever else you save the data) is under your app control as well. Assuming that whatever local storage is protected to your app only (whether natively by Android OS such as private prefs or otherwise), you can simply save the local cache under the user id of the currently authenticated user. So your app can check if the authenticated user matches that user id of the cache and if it does not, then access is denied. In fact, the local cache should be keyed minimally by the logged-in user so you get that kind of check for free (basically the snapshot data won't be found).

Resources