Implementing a Role-Based Access System for Authorization
-
I'm trying to check if a user has a certain role to verify whether they're allowed to do something, and I'm trying to find the correct way to do this. I'm running FA 1.17.5 locally because I'm still test-driving it.
When the user logs in, they get a JWT. This gives me a user ID. I can then query the API to get the group(s) that the user is part of. Querying a single user via the API gives me one or more group IDs. I can then query the API again so I get the roles that are attached to that group. However, I'd have to run this query several times in order to find whether the role I'm looking for is in that group, because unless I missed something the group API doesn't allow searching for multiple IDs. I can of course get all the groups and search through those in the code, but there is probably a better way. I've noticed that Roles don't have their own API endpoint.
The solutions I see are:
- using registrations (which ties roles immediately to users, and as a result, they're immediately available).
- using an ElasticSearch query and specify multiple groups so I can get the roles from there (somehow)
Ideally, I'd want to be able to find out in a single query whether a user has a certain role or not so that I can find out whether they're allowed to do something.
It could be that I'm kind of mistaking roles for permissions, so I was wondering what the philosophy was, and what approach I should be using
-
Hmmm. The easiest way to do this is to use the JWT populate lambda to put the roles in the JWT.
https://fusionauth.io/docs/v1/tech/lambdas/jwt-populate
I'd use a lambda like this:
function populate(jwt, user, registration) { if (registration && registration.roles) { jwt.roles = registration.roles; } }
This should include any group roles if the user is a member of the group and is registered to the application the roles are defined in.
Then when whatever is consuming the JWT examines it, they should see the roles as an array in the JWT, and can check there.
Sorry if that isn't clear. Does that do what you want?
PS I'd also recommend upgrading to 1.18.5. Lots of bugs fixed in this release.
-
Thank you very much for your prompt reply. I haven't explored the Lambdas yet, so I'll look into that.
It's not so much that I immediately need it in the JWT itself (though that is a nice bonus - an additional request would've been totally acceptable) but this should do the trick. I was mostly wondering whether this was the intended way to use the roles.
I'll definitely upgrade!
-
Ah, I just tested this out and if you don't need it in the JWT, you should be able to see it in the registrations object returned after login.
Here's a response I get after logging in:
{ "token": "ey...", "user": { "active": true, "connectorId": "e3306678-a53a-4964-9040-1c96f36dda72", "email": "email@example.com", "id": "2df13f18-01cc-48a4-b97a-2ab04f98d006", "insertInstant": 1592857899119, "lastLoginInstant": 1596819645662, "lastUpdateInstant": 0, "passwordChangeRequired": false, "passwordLastUpdateInstant": 1592857899145, "registrations": [ { "applicationId": "78bd26e9-51de-4af8-baf4-914ea5825355", "id": "73d2317b-d196-4315-aba2-3c205ed3ccae", "insertInstant": 1592857899151, "lastLoginInstant": 1592857899153, "lastUpdateInstant": 1596813810104, "roles": [ "Role1" ], "usernameStatus": "ACTIVE", "verified": true } ], "tenantId": "1de156c2-2daa-a285-0c59-b52f9106d4e4", "twoFactorDelivery": "None", "twoFactorEnabled": false, "usernameStatus": "ACTIVE", "verified": true } }
So
user.applicationId.roles
is what you want. Note that roles are applied on an application by application basis. If a user is in a group which has a role 'roleA' which is created in 'applicationA', but is not registered for 'applicationA', they won't receive that role. More on that here: https://fusionauth.io/docs/v1/tech/core-concepts/groups