Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you keep your custom claims in sync with roles stored in Firebase database

This question is lifted from a comment on another thread and I figued that it was a good question that yet didn't have any answer on SO.

How would i link my users in auth, to their specific profiles in the database? (profile contains, full name, usertype, etc.)

The context of the question is referring to the different strategies of storing user auth access logic in the database and/or as custom claims. And how do you maintain those two to be in sync (Is there any function in User Authentication for User Roles?).

like image 737
Dennis Alund Avatar asked Dec 18 '22 18:12

Dennis Alund


1 Answers

Custom claims must be set with the admin SDK, which already hints that cloud functions can be utilized to set claims according to your logic.

If the user profile data contains the user type and roles, you can create a trigger for updates on the user object. If you store your roles as an array you can implement a function like this.

functions.firestore.document('users/{uid}').onUpdate((change, context) => {
  const uid = context.params.uid;
  const userBefore = change.before.data();
  const userAfter = change.after.data();

  const hasDifferentNumberOfRoles = userBefore.roles.length !== userAfter.roles.length;
  const hasOtherRoles = userBefore.roles.some((role: string) => userAfter.roles.indexOf(role) === -1);

  if (hasDifferentNumberOfRoles || hasOtherRoles) {
    return admin.auth().setCustomUserClaims(uid, { appRoles: userAfter.roles});  
  }

  return null;
});

This ensures that every time the user roles in data base change, your custom claims auth will also change.

Arrays are convenient for roles since it's easy to query Firestore to find users with certain roles using array-contains and it's also easy to check roles in your database rules as a list.

service cloud.firestore {
  match /databases/{database}/documents {

    match /protected-content/{documentId} {
      allow read: if hasUserRole('admin');
      allow write: if hasUserRole('super-admin');
    }

    function hasUserRole(role) {
      return role in request.auth.token.appRoles;
    }
  }
}

A small side-note about storing your roles in a appRoles attribute on the custom claims, is that it makes in conveniently easy to remove all current roles if you change your structure or want to replace all roles for a user.

like image 196
Dennis Alund Avatar answered Dec 28 '22 09:12

Dennis Alund