You general approach around the three steps is good practice.
Please consider Firebase is cloud based so its exposure is front and center to the world, here are some specific recommendations to firebase and how to introduce watch dog practises:
// Allow read/write access to all users under any conditions
// Warning: **NEVER** use this ruleset in production; it allows
// anyone to overwrite your entire database.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
Look out for rule cascading , make sure you traverse the entire tree to check rules
{
"rules": {
"users": {
"$uid": {
".read": "auth.uid == $uid || root.child('users').child(auth.uid).child('isAdmin').val() == true",
".write": "root.child('users').child(auth.uid).child('isAdmin').val() == true",
".indexOn": ["email"]
}
}
}
}
Like the code below, since authorization on create allows malicious users created (perhaps by bots)
IMHO - I would make this manual or atleast have it checked against the users registration IP against something like honeypot
const getCustomClaimsByEmail = require('../utilities/get-custom-claims-by-email');
const setCustomClaims = require('../utilities/set-custom-claims');
module.exports = ({ admin, environment }) => user => {
const db = admin.firestore();
const usersCollection = db.collection(environment.schema.users);
const customClaimsRef = admin.database().ref(environment.schema.customClaims);
const auth = admin.auth();
const email = extractEmailFromUser(user);
return Promise.resolve()
.then(getCustomClaimsByEmail(customClaimsRef, email))
.then(setCustomClaims(auth, user.uid))
.then(claims => {
const update = mapUserUpdate(claims, user);
return usersCollection.doc(user.uid).set(update, { merge: true });
});
};
function mapUserUpdate(claims, user) {
const email = extractEmailFromUser(user);
return {
claims,
email,
emailVerified: user.emailVerified,
lastSignInTime: user.metadata.lastSignInTime,
creationTime: user.metadata.creationTime,
providerData: user.providerData,
};
}
function extractEmailFromUser(user) {
return user.email || user.providerData.find(({ email }) => email).email;
}
Now to your DDOS / traffic,