On a form submit I'm creating two document:
first document is a
userDoc(uid): { username, displayName, photoURl }second document is a
usernameDoc(username): { uid }
The process:
the user logs in, creating a user object in local storage with their uid
give the ability to the user to choose a custom username
once the user selects a valid username, we save two docs to firestore:
- userDoc with the user.uid as the doc id and it has username, photoURl and displayName as fields.
- username doc with the username as its doc id and the user.uid as its data
// user is a firebase auth user object from localstorage.
const { user, username } = useContext(AuthContext);
const handleSubmit = async (e) => {
e.preventDefault();
// create a userDoc with the name of "user.id"
const userDoc = doc(firestore, "users", user.uid);
//create a username doc with the name of "formValue" , letting the user select their own username
const usernameDoc = doc(firestore, "usernames", formValue);
// write both docs together as a batch
const batch = writeBatch(firestore);
// reverse mapping.
batch.set(userDoc, {
username: formValue,
photoURL: user.photoURL,
displayName: user.displayName,
});
batch.set(usernameDoc, { uid: user.uid });
await batch.commit();
};
We allow anyone to read the usernames docs in our firestore.rules:
match /usernames/{username} {
// anyone can read the username doc
allow read;
// can only create if valid username : see isValidUsername()
allow create: if isValidUsername(username)
}
but they can only do other operations only if their current uid matches the uid of the document they are trying to update.
for example creating a post under their uid:
function canCreatePost(userId) {
let isOwner = request.auth.uid == userId;
let isNow = request.time == request.resource.data.createdAt;
let isValidContent = request.resource.data.content.size() < 2000 && request.resource.data.heartCount == 0;
let username = get(/databases/$(database)/documents/users/$(request.auth.uid)).data.username;
let usernameMatches = username == request.resource.data.username;
return isOwner && isNow && isValidContent && usernameMatches;
}
If anyone can read the username doc, subsequently getting access the user's uid only by having access to their username. i.e I can get the uid by only having the username of the user.
Won't that enable people to simulate a user by only having access to their username?