Firestore Rules reverse mapping security keyhole

Viewed 15

On a form submit I'm creating two document:

  1. first document is a userDoc(uid): { username, displayName, photoURl }

  2. second document is a usernameDoc(username): { uid }

The process:

  1. the user logs in, creating a user object in local storage with their uid

  2. give the ability to the user to choose a custom username

  3. 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?

0 Answers
Related