I'm currently trying to figure out sign-up for my app. Right now, at sign up I ask users for a username alongside an email and password (for firebase_auth).
The thing is, I don't want more than one user with a username, so I need to check my database if there already exists a user with that username before signing up the user with firebase_auth and adding this new user to my database.
I'm concerned about a race condition that could arise if two users try to create an account with the same username at the same time. I'm trying to use TransactionHandler, but im not sure exactly how I can do this as I hear that a transaction might be run up to 5 times, and we shouldn't do anything that should not be run multiple times (i.e. sign up with firebase_auth?).
Any ideas as to how I can work around this?
There is no way to create a user account and create a document in the database atomically. So you'll have to instead find a way to deal with it in your application code.
Typically this comes from thinking of account creation as a sequence of steps. For example, this is quite common:
Create account in Firebase Authentication, based on the credentials the user enters.
Have the user verify their email address, so that you can reach them.
Have the user claim their unique user name.
You'll see that none of these steps depends on a step that comes after it, so you can execute them in order. And when you do that, step 3 should work fine in a transaction that may run multiple ties.
Just keep in mind: if you want something to be unique on Firestore, you need to use that value as the IDs of your documents. There is no way with client-side access (not even with transactions) to guarantee uniqueness of values across documents. For some more questions on that topic, see:
Firestore unique index or unique constraint?
Cloud Firestore: Enforcing Unique User Names
firebase rule for unique property in firestore, which uses a single document to store all user names.
Prevent duplicate entries in Firestore rules not working
Related
I've started designing a referral system and wanted to use firebase auth UIDs as the referral code for each user, however they're longer than I'd like and also not guaranteed to stay small. I've looked into some libraries for short text compression, hashing, etc; But none seem to satisfy my needs. I've recently came across the lovely short-uuid pkg on npm but unfortunately it doesn't seem it works with firebase UIDs(because those aren't UUIDs) but i've been looking for a possible solution that doesn't involve keeping a lookup table of custom IDs to UIDs.
So the real question: is there any good way to compress a short string programmatically and then decompress?
There is no way for you to control the UIDs that Firebase Authentication generates in its various providers.
If you want to use a shorter/friendlier scheme for identifying your users, some options are:
You can generate a shorter ID yourself, and maintain a mapping of those IDs to the ones that Firebase generates. You'll typically want to include a uniqueness check for your shorter IDs, as the chances of collisions rapidly go up for the shorter strings.
A common example of a friendlier identifier for users is to allow users to pick a unique username. This is essentially a variant of the first option, but now with a user-selected ID. Here too you will need to perform a check to prevent duplicates.
You can also creating a custom provider that plugs into Firebase Authentication. When doing this, you control the UID that Firebase uses too. Here you are responsible that the UIDs your provider generates are unique not just within your own provider, but across all providers you've enabled for your project.
As #FrankVanPuffelen explained there is no way for you to control the UIDs that Firebase Authentication automatically generates.
But with the createUser() method of the Admin SDK you can define which UID you want to assign to a new user. As explained in the doc, "if you instead want to specify your own UID for the new user, you can include it as an argument passed to the user creation method"
await admin.auth().createUser(
{
uid: 'some-uid',
email: 'user#example.com',
password: '....',
}
);
You can run this code in a Cloud Function or on a server you own.
Of course, you need to ensure that the user's UID is unique for each user.
I am confused on the insistence in the documentation to have users sign on to give authentication. With a card game app, I want users to get on easily (without a need to sign on), find a game to play in, and play the game. As cards are played, each turn is recorded in Firestore, and all users notified to read the changed parameters. So all users can read, write and update.
Even if I did make users sign on, anyone could create an account, and I would be no further ahead except I have turned away some game participants.
I know I can set rules so that all data is a small size. I maybe check to ensure no data is stored after a certain time without being used, and I could delete the collection for that game. Is is it this sort of procedures I use to guard the database from growing too big, or a 'bad' user working out where the data is, and overwriting data for some games.
If you want a low-friction way to get users signed in, use anonymous authentication. It will give the user an account with a UID without requiring any prompts. You can then use security rules to protect per-user information, since anonymous accounts have a UID just like normal accounts. You can then decide to let the user upgrade that account when they want a more permanent identity in your game (since anonymous accounts become inaccessible if the user clears their device local storage).
Firebase is great as it offers a lot of authentication providers. In one of my apps, I use four different providers provided by Firebase (Email, Twitter, Facebook and Google), but I also need to let users sign in via LinkedIn.
As Firebase SDK does not offer LinkedIn, I need to implement the login flow manually, which doesn't seem to be difficult, but there is one huge issue which I see. During the creation of a custom JWT token, I need to assign a user ID. And I have no idea how to generate one while making sure that my approach will not conflict with user IDs which Firebase generate on its own for other providers.
For example, let's imagine that a user Andriy Gordiychuk signs in via LinkedIn and his email address is andriy#gordiychuk.com. A simple way to create a user ID would be to take an email address (andriy#gordiychuk.com) and to randomise it using some hashing function. I would get some random id such as aN59nlphs... which I would be able to recreate as long as the same user signs in. So far, so good.
However, how can I be sure that the ID which I get is not already used by another user who signed in via Twitter, for example?
One way to mitigate this issue is to store LinkedIn user IDs in a Firestore collection. Then, when I need to create a token, I first check whether I already have an ID for this user. If not, I would hash the email address, and I would try to create a user with this ID. If this ID is already occupied, I would then try to create another ID until I stumble upon an ID which is not occupied, and I would then use it.
I don't like this approach for two reasons:
Although the chance that I would generate an already occupied ID
is small, theoretically the process of finding an "available ID" can
take a lot of steps (an infinite loop in a worst-case scenario).
Once I find an available ID, I must store it. Given that all these calls are asynchronous there is a real chance that I would create a user with a suitable ID, but because the save operation fails, I would not be able to use this ID.
So, does anyone know how to choose user IDs for such use case correctly?
It's fairly common to generate a string with enough entropy (randomness) to statistically guarantee it will never be duplicated. This is for example behind the UUID generators that exist in many platforms, and similarly behind Firebase Realtime Database's push keys, and Cloud Firestore's add() keys. If there's one in your platform, I recommend starting with that.
Also see:
The 2^120 Ways to Ensure Unique Identifiers, which explains how Firebase Realtime Database's push() works.
Universally unique identifier, Version 4 on Wikipedia
the uuid npm module
this is a bit of a composite question, I'll try my best to separate the various parts even if they all have a common intent.
Common intent
Having a clear way of delivering notifications about posts regarding specific topics to Firebase Users (and not simply to application instances).
I have tried various methods, and I can't find a definitive answer about which one is the best one.
Method 1 - Relying only on the database
Each Firebase User has its own document in the Firestore at users/{userId}
This document contains two collections: tokens and interests.
The token collection contains a list of documents which have FCM tokens as one of their fields. Each time an user signs in the application or FirebaseInstanceIDService.onTokenRefresh() is called, the collection is updated to add the new token.
The interest collection contains a list of interests which simply are strings and are used as tags for posts. This collection has a mirror as interests/{interestId}/users/{userId} showing all the users interested in something. (This is kept updated and synchronised via a Cloud Function)
When a new post is created under a specific interest, I can get a list of all the users interested and then get their tokens from their document. Finally, I send a notification to each individual token.
Problems
This solution is not elegant (this isn't that big of a problem)
With the new GDPR rules I fear I might not be allowed to save tokens
directly on the Database
If the user signs out when he's offline, the
token isn't removed from his document, and the new user receives
notifications for the old interests.
Should I keep track of what the current token is and update it each time an user signs in ignoring FirebaseInstanceIDService.onTokenRefresh()? Else only the user signed in when the service is called would update the database.
Method 2 - Using FCM topic subscriptions
This should be the best option for me, but I can't understand how to make it work with multiple users on the same phone (always one at a time though)
The way I would handle this is still have the users/{userId}/interests collection, removing users/{userId}/tokens and interests/{interestId}/users, and subscribe/unsubscribe from the various topics as the user signs in and out.
Problems
What happens if the user signs out when he's offline? There is no way to retrieve the current subscriptions and remove each one, potentially resulting in conflicting topics subscriptions.
Thank you very much for your time
Consider the following case:
User got invitation ticket, after they invite person, the number will decrease. The user can't add the invitation number by their own. Based on the Firebase (Within Function), it seems that it is not suitable for this use case, because once I grant access for user to modify the invitation number for user, they can somehow increase the number or assign any number they want. Is my understand correct? Thanks.
No, security rules can allow you to restrict the data written to the realtime database and by whom.
If you need trusted administrative logic, such as keeping counters you can use Cloud Functions for Firebase or the admin-sdk on a traditional backend.