Since there is no way to implement a complete OAuth2 flow in Flutter web (because of this issue), I'm using a cloud function as the callback listener. The process is initiated by the user in the web app and, instead of the web app listening for the callback, the redirect url is that of a cloud function that unpacks the token and stores it in Firestore.
What would be a good way to relate the request the auth server makes to my cloud function to the individual user which is registered in my app's database? In other words: how does the firebase cloud function know for which user in Firestore to store the access token?
My idea is to make another wrapper function that gets called from the web app when the user initiates the flow (receiving the user id as parameter) and have the function that receives the token from the external service also call this wrapper function, but this doesn't scale well (if you have multiple users at the same time, it could misplace each other's tokens).
Related
I have a project that requires the usage of login via Facebook and Google, so I picked Firebase to speed up the process of authentication; However, the user must also be able to login via LINE(a messaging app popular in SEA).
Currently, I have implemented:
Line Login-> Finish Login -> Redirect to Cloud Functions -> Verify Line's Access_Token -> Create a User In Firebase from LINE's ID Token -> Create Custom Token from Firebase User
All that's left is to implement signInWithCustomToken and then get the idToken from firebase to send to my api for authorisation.
Now, the signIn process should be handled by the client side, but I am not sure how to pass the customToken to the client side(Our backend api and frontend client is separated).
I could pass it in a query param as I redirect the user to the frontend, but I am not sure if that's the best way to go about it.
What should I do?
After the user has logged in to LINE, it redirects to any link you set
for it to redirect to with the url params being the authorisation code
and state, so ?code=xxxxxx&state=xxxxx ... I decided to use cloud function to be the redirect url for the LINE Login
If I understand correctly, instead of directly redirecting LINE to the Cloud Function, you should probably redirect to a page of your web application which, in turns, calls a Callable Cloud Function (passing the code and statevalues as arguments).
This Cloud Function verifies the token as well as creates a user in Firebase and when this is done, sends back the token to the page of your web application. You then have all the elements to call the signInWithCustomToken() method.
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)
I'm using firebase cloud functions with the admin sdk in order to perform some operations on the realtime database on behalf of the user.
For example, if the user wants to store a new parameter on the database, it will press a button on its android client which relies on a directly callable function (onCall). This callable function will save the parameter in the right location in the database.
Now, I understand the onCall function receives automatically some info about the user in the context parameter, such as the user id and the token, but I don't know if these parameters are used to control the effective identity of the user.
Since the function is running with the admin sdk in full privilege mode, I want to be sure that the user is who he claims to be. In a scenario in which one user steals the uid of another user, and calls the function with this uid but with its own Auth token, he will be detected?
Does the function check if the user id (uid) is compliant with the Auth token?
If yes, the only thing I have to do is to check if context.auth is different from null?
Just to move from the comments, the context.auth.uid value is validated server side, so you can trust that the user hasn't manipulated the value. (See the code here.) Now, when you use the firestore or realtime db admin API, the function has access to any value in the database, so you do need to ensure the user has access to the value in the DB (i.e. add a where uid='uid').
I have following use cases:
I have cloud functions which are accessible with HTTP endpoint and they use authorization using custom token because the app is only accessible with certain IPs stored in RTDB so I have created one cloud function with will generate a custom token after signing in user using firebase client SDK and then it will create a custom token using admin SDK after checking IPs which are stored in RTDB.
Now with every subsequent call client will send token and functions will serve the request.
I have event listeners bound with the RTDB on a client and use file upload using client SDK which client initialize with firebaseApp.auth().signInWithCustomToken(custom token).
On the function side I use the same sign in the method that also utilizes my firebase SDK and then I serve that request. The problem here is this sign in the method is very slow like it is taking generally more than 1 second only in sign in.
Alternative
Now alternative is I can use id token which can be created using currentUser.getIdToken() on the client side itself and it takes barely few ms to decode that token but I cannot initialize SDK with that token. so I have to use admin SDK but my IP node in RTDB is not accessed by the normal user and can only be accessed with admin SDK, so if I use ADMIN SDK with Control Access with Custom Claims and Security Rules to give admin SDK similar access that the authorized user has then IP node will not be accessible.
Issues with id token
Id token can be refreshed on the client side so once a client has a custom token, It can generate as many tokens it wants and that is not desirable. Apart from that validating IP everytime is not the operation that I wanted to do so with custom token I only use that in generating a custom token and then for a refreshing token but with id token, this would not be possible as the client can generate it with SDK.
Basically, I have to use firebase SDK on the client side which will need custom token(for additional authorization check) to initialize and at the same time I call the clound function from the similar app so what is the best way to implement this use case.
How can I create a firebase user from the node.js client? I see that there is a simple-login but that looks to be used from the web browser. I would like to authenticate with my firebase secret and then call the createuser api somehow.
The way my system is built the clients only send requests to the backend for processing. This way I have a log of every action taken by every user and I can guarantee that each alteration is applied to my other databases as well before it makes it into firebase. Also I do not want users to be able to create other users. Firebase is just a workqueue for me mostly but I am also using the simple login to verify the user then swapping them over to a login token afterwards to get the ability to check custom permissions in the auth on security rules.