Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Roles and Permissions (AUTH_GET_PERMISSIONS) #36

Merged
merged 1 commit into from
Jul 25, 2019
Merged

User Roles and Permissions (AUTH_GET_PERMISSIONS) #36

merged 1 commit into from
Jul 25, 2019

Conversation

LaszloDev
Copy link
Collaborator

The work that is done with this library is a great help for me to connect to my firebase backend.

There are a few parts missing which I encountered, like the possibility to work with user roles and react-admins permissions property in connection with the Firebase custom claims. I'm going to try to provide the missing parts which this first pull request.

About Firebase custom claims see:
https://medium.com/google-developers/controlling-data-access-using-firebase-auth-custom-claims-88b3c2c9352a
and https://firebase.google.com/docs/auth/admin/custom-claims

Problem

My application does have different user roles and thereby I need to restrict access or disable some resources and views.
Todo so React-Admin provides a permissions prop which can be used to dynamically restrict access.
https://marmelab.com/react-admin/Authorization.html#restricting-access-to-resources-or-views

Behavior

Returning a single child in the <Admin /> component does only render the Login view. Every login attempt does automatically dispatch the AUTH_LOGOUT-action.

<Admin
       authProvider={authProvider}
       dataProvider={dataProvider}
       title="Memberarea"
        >
          {permissions => [
             <Resource
               create={MemberCreate}
               edit={MemberEdit}
               key={'member'}
               list={MemberList}
               name={'members'}
               options={{ label: 'Members' }}
             />,]}
</Admin>

image

Solution

The react-admin-firebase AuthProvider does not implement the AUTH_GET_PERMISSIONS-action as needed to return the permissions to the reducer.
https://marmelab.com/react-admin/Authorization.html#configuring-the-auth-provider

Pull request

I implemented the missing action action and added HandleGetPermissions() to extract the server side / backend provided user roles inside the Firebase User token as "claims". The implementation is running locally in my application very good and I can set the user roles with a Firebase function for a specific user.

Maybe you see other ways to provide user roles into react-admin but this seams to me the most forwards and in alignment with Firebase recommendation for user roles.

Happy to hear your feedback!

@LaszloDev LaszloDev mentioned this pull request Jul 24, 2019
@benwinding
Copy link
Owner

Hi @LaszloDev,

Thanks so much for the detailed writeup! Glad the package helps you out!

This is a great idea, and as I've spent more time in the Firebase ecosystem, I've learnt that UserClaims are the best way to securely connect Permissions to the Firebase user.

Although UserClaims are not easy to change on the client (maybe not even possible without the firebase-admin package), this looks like a step towards better permissions.

Cheers,
Ben

@benwinding benwinding merged commit 21d4669 into benwinding:master Jul 25, 2019
@benwinding
Copy link
Owner

This has been deployed as version: react-admin-firebase 0.8.5

commit: 2efcf09

@LaszloDev
Copy link
Collaborator Author

Thanks for merging!

Although UserClaims are not easy to change on the client (maybe not even possible without the firebase-admin package), this looks like a step towards better permissions.

Right, the claims can only be set at a server level and I didn't like the suggestion to use a callable Cloud Function.

So I wrote a first version of a Firebase function which allows to set custom claims by editing a Firestore collection. Each document in the Roles collection represents a User and its properties are his roles. The collection is secured by allowing only users with Admin-role to write to.

exports.setUserClaimsOrRoles = functions
  .firestore.document('config_roles/{userId}')
  .onWrite((change, context) => {
    const userId = context.params.userId
    const changeAfterData = change.after.data()
    let data = {}

    console.log(`Changing claims for userid: ${userId}`)

    if (typeof changeAfterData === 'object') {
      data = { ...changeAfterData }
    }

    // Remove falsy values from object
    cleanObject(data)

    return admin
      .auth()
      .setCustomUserClaims(userId, data)
      .then(() => {
        admin
          .auth()
          .getUser(userId)
          .then(userRecord => {
            // The claims can be accessed on the user record.
            console.log(
              `New claims/roles for user ${userId} are`,
              userRecord.customClaims,
            )
          })
          .catch(error => {
            console.log(error)
          })
      })
      .catch(error => {
        console.log(error)
      })
  })

function cleanObject(obj: { [index: string]: any }) {
  Object.keys(obj).forEach(key =>
    obj[key] && key.includes('role_') ? null : delete obj[key],
  )
}

The user would need to logout and login again to get a fresh Firebase token with the custom claims/roles to work with react-admin.

Do you see any problems or optimizations? Otherwise we could put it in the readme with an example how to use Firebase roles with react-admin-firebase.

@benwinding
Copy link
Owner

So I wrote a first version of a Firebase function which allows to set custom claims by editing a Firestore collection. Each document in the Roles collection represents a User and its properties are his roles. The collection is secured by allowing only users with Admin-role to write to.

Wow, that's actually a brilliant idea!

Currently, at my work we invoke cloud functions to modify user claims, but it's tedious and permissions have to be stored in the database as well anyway.

I generally don't like Firestore triggers as they are hard to debug and can create a chaotic system if abused (we had like 30 running!). But this looks like perfect application for them!

@LaszloDev
Copy link
Collaborator Author

Happy to hear you like it. Took me some time and looking at a lot different approaches to come up with it. The only thing I don't like so much is that this needs it own Collection for Cloud Functions to be able to monitor it and have a clean code. Have to check if that works with a sub-collection to have only one config collection within the Firestore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants