I have a simple mechanism for authenticated users using Firebase Authentication
. then I would like to create a collection users
with a document referring userID
of the authenticated user using Firebase Cloud Firestore
.
Here is the code. -- with a bit of TypeScript, I'm using NextJS as framework but I don't think it really matters for the question:
const clientCredentials = {
...
};
const firebaseApp = initializeApp(clientCredentials);
const firestore = getFirestore(firebaseApp);
interface SignUpFormValues {
username?: string;
email: string;
password: string;
}
export const createUser = (signUpValues: SignUpFormValues): Promise<any> => {
const auth = getAuth();
const { email, password, username } = signUpValues;
return createUserWithEmailAndPassword(auth, email, password)
.then((userCredential: UserCredential) => {
const { uid } = userCredential.user;
const userRef = { username };
return setDoc(doc(firestore, "users", uid), userRef, { merge: true });
})
.catch((error) => {
// error handling flow
throw error;
});
};
As it is, everything is working fine but I'm not satisfied with the result at all.
i.e, if there is an error while setting a up a new document for my users
collection, but the authentication part went well, and I try to invoke the createUser
again, it will fail as the user is already created and authenticated.
Now I also have a listener setup somewhere else in a code, that check if the current user if authenticated or not.
Same here, straightforward code.
useEffect(() => {
// Listen authenticated user
const auth = getAuth(firebaseApp);
const unsubscriber = onAuthStateChanged(auth, async (user) => {
try {
if (user) {
// User is signed in.
const { uid, displayName, email } = user;
// In theory I could setup the document here.
setUser({ uid, displayName, email });
} else setUser(null);
} catch (error) {
// Most probably a connection error. Handle appropriately.
} finally {
setLoadingUser(false);
}
});
// Unsubscribe auth listener on unmount
return () => unsubscriber();
}, []);
I could potentially creating the user collection here, but it feels kinda wrong because I cannot do if (doc(users/${uid}).exists()) createNewUserCollection()
. Instead I have to do setDoc
, with { merge: true }
option.
My question is double:
- Is there an official way of handling this somewhat common use-case?
- Could I use a mechanism that "rollback" my authenticated user if the creation of the user collection did not work in the first place?
Any other suggestion is welcome.
CodePudding user response:
Is there an official way of handling this somewhat common use-case?
No it is not possible to combine in one atomic transaction the user creation in the Auth service and the doc creation in Firestore
Could I use a mechanism that "rollback" my authenticated user if the creation of the user collection did not work in the first place?
You could schedule a Cloud Function that regularly checks that the users lists in the two services are in sync. If not, from the CF you can very well delete from the Auth service the users that are not in the Firestore collection.