I have a use case where a user of our .NET Core API can belong to multiple "organizations" and have different roles within those orgs.
For example, if they POST to a Location/Create endpoint on behalf of OrganizationId = 1, they should have Admin privileges. However, if they try to access that endpoint on behalf of OrganizationId = 2, they should have only basic user privileges. The user can perform these actions without logging out and logging in again.
I would like to write some middleware to make a DB call and retrieve the role for the user in the organization they are attempting to modify, then use the .NET Core Role syntax on controllers to restrict access at that level.
I have not been able to find an example of a case where middleware modifies the users role before the request pipeline reaches the controller action, on each request. Does anyone have experience with a similar authentication scheme?
Instead of modifying the user's role in the middleware, have you considered keeping a permission model in the database that maps a given user's role for each org? You could then allow/deny requests in the middleware based on the domain requested and the user's permission level in that domain.
For example, John Smith belongs to two organizations with ID 1 and 2. The user permission model could look something like this:
UserPermissionLevel
------------------------------------------------
| Uid | Name | OrganizationId | RoleId |
|----------------------------------------------|
| 1 | John Smith | 1 | 1 |
| 1 | John Smith | 2 | 2 |
| 2 | Jane Doe | 2 | 1 |
------------------------------------------------
And you'd have a separate table containing the Role permissions like so:
Role
-------------------------------------
| RoleId | Name | CanGet | CanPost |
|-----------------------------------|
| 1 | Admin | True | True |
| 2 | Basic | True | False |
-------------------------------------
Using this model, you could query these tables in your middleware to determine the appropriate access level.
public class OrgAuthorizationMiddleware
{
...
public async Task Invoke(HttpContext context, MyDbContext databaseContext)
{
// Check that the user has the requested role and the
// appropriate permission for the request as needed.
// Add additional request type logic to check user's
// specific permissions from UserPermissionLevel.
if databaseContext.Role.Any(x => x.RoleId, RoleId)
{
await _next.Invoke(context);
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
}