Can you trigger (firebase) cloud functions on auth login (but not account creation)? - firebase

It seems like the firebase auth triggers are onCreate and onDelete. I'd like to also fire some kind of trigger on non-create login (basically periodically syncing the user's avatar and display name and such with what's in the database).
I can fake this by just doing it on the client side, which is what i'm doing right now - just updating the user record with whatever is in firebase.auth.user. Excitingly, this can't be done right after login, since if the login is also account creation, the user record tends to not exist (since it is created via a triggered cloud function), and I can't tell from auth.signInWithPopup() if the resultant signin was a creation or login event.

There is no such trigger. As you've observed, it just provides onCreate and onDelete.
Firebase Auth doesn't provide a way to sync the user's avatar with their authentication provider. It just copies the URL once at the time the account was created.
If you need to update the user's profile picture, you will have to do that yourself by calling updateProfile() on the user object and provide a URL for the picture.

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)

Firebase Auth subscription

I'm working on a app which uses Firebase Auth to signup and login, but I'm facing some things which I don't know how to start. Users need to registrate on a website and they need to pay a subscription before the user is created in firebase, when they don't pay anymore, the user account should be disabled. So basically, users registrate on the web and after they pay, they can log into the app with their credentials.
Edit:
Since yesterday I'm trying to implement either mollie or stripe, but I can't get myself started, online there are very few video's about payments in combination with firebase
There are basically two ways off the top of my headto do this:
A) Secure but it involves cloud function and creating custom authentication token to login.
User registers with email.
User keys in login information and posts to cloud function.
Find user's uid/email and check for password.
Fetch the subscription document and check if it is active.
If it is inactive, return an error message accordingly.
If it is active, create an authentication token and return to user to login.
B) Client side checking, less secure but will do the trick.
User logins
Fetch subscription using user's uid. Check its validity
Force redirect user to subscription page if it is inactive with
error message. OR Autologout user if it is inactive with error message.
May I also suggest Stripe for their subscription service (Not sponsored)? Unless you already have an implementation in place.

Firebase Function onLink trigger

Is it possible to implement a function hook on when an anonymous user is linked to a normal account?
What I am trying to achieve in a secure way is:
1. DeepLink for invitation opens in the app.
2. Get invited_by from the URL and sign in the user anonymously, saving invited_by to invitations/anonymous_user_1/invited_by.
3. User registers an account and signs in so onCreate function is triggered, but we do not have access to the anonymous account there.
4. If there already is an anonymous account before registering, we link the anonymous account to the users new account.
And here is where I want to trigger a remote firebase function (don't want to trigger it on the client side, because it could be retriggered and abused to gain invitation advantages).
onLink() should do something like this:
1. Get old account (anonymous) and new account.
2. Get invitations/anonymous_user_1/invited_by from anonymous account.
3. Store it and perform actions on the new account.
4. Delete invitations/anonymous_user_1/invited_by once processed.
Is there a way to do this server side? Because onLink() would only be called once, and therefore I could process the anonymous data once.
As I said I would not want to call a function locally.
Thanks
There is no Firebase Authentication trigger for Cloud Functions when a user links accounts from multiple providers at the moment. As Doug commented, it's best to file a feature request for this.

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.

Keeping emails synchronized between Firebase auth and database

I am using Firebase Web for a SaaS solution. My purpose is to have access to users' email at any time, either for sending notifications or triggering alerts from the backend.
For security reasons, Firebase auth does not allow to list users' email or to fetch emails based on user IDs. As a consequence, I keep a copy of the email into a specific collection in the Firebase database when a user account is created. The copy is made by a Cloud function that is triggered on user creation (following guidelines: https://firebase.google.com/docs/auth/extend-with-functions).
Thanks to the copy available in the Firebase database, I can access users' email. However, my issue is when a user changes his email.
Firebase auth provides the updateEmail function that returns a promise. I use this last to update the email in Firebase auth. Then, when the promise resolves I update the user email in the Firebase database. However, this has a major drawback: all the logic is performed on the client side and since both operations are not performed in a transaction if the client refreshes or closes his browser (or assume it crashes), then it is possible that Firebase auth is updated but not the Firebase database, thus leading to an inconsistent state.
I looked at the documentation, expecting the possibility to trigger a Cloud function when user auth information is updated. Unfortunately, I cannot find such a feature.
Another solution I thought about is to update the database from the Web client. Then, this last triggers a Cloud function that updates Firebase auth with the admin SDK. This last solution works but bypasses the check performed by updateEmail that ensures the new email is not used by another account. Also, the account hijacking protection performed by updateEmail is evicted, which is really bad from a security point of view.
Any idea to solve this problem properly is welcome.
Here are a couple of options:
When calling updateEmail, update the email in your database first before calling updateEmail. THowever, if an error occurs, you need to catch it and undo that change in your db.
When a user wants to updateEmail, send their id token and new email to your server or firebase function http endpoint. There you verify the ID token with the admin SDK, then use the client SDK require('firebase'), using the uid from the ID token, admin.auth().createCustomToken(uid), then using client SDK, firebase.auth().signInWithCustomToken(customToken). You can then call user.updateEmail(newEmail) on the backend and save the email.
Always save the uid only and just use Admin SDK admin.auth().getUser(uid) to look up the user and get their email. This guarantees you get the user's latest email as you will not be able to catch the email revocation if the user chooses to do so.
No need to save anything. Use the CLI SDK to download all your users and their emails. Check https://firebase.google.com/docs/cli/auth#authexport
This is also better as you will always be able to get the latest email per user even if they revoke the email change.

Resources