Implementing Role Based Access Control (RBAC) in AWS Environment
Role-based access control (RBAC) refers to the idea of assigning permissions to users based on their role within an organization1. This document covers few RBAC implementation techniques for an API deployed in AWS environment.
RBAC logic separated from application code
In this approach, logics related to role based access control is separated from application code. An example would be an AWS API Gateway handling authentication for Lambda backend.
Using API Gateway with custom Lambda authorizer
Custom lambda authorizer can be used to authenticate API requests using custom validation logic. API gateway can be configured to send request information (headers, path, query parameters etc.) to a lambda function to decide whether to allow or deny the request.
Following are some requirements to implement RBAC using custom lambda authorizer:
- An attribute that can be used to identify the role of the user (id, group etc.)
- A data store (DynamoDB, config file etc.) which holds a mapping between role and the resources which it can access
- For more granular permissions, the request method (
GET
,POST
,DELETE
etc.) can be used to manage read/write permissions3
- For more granular permissions, the request method (
The lambda event for authorizer lambda function will contain useful metadata of the request. It can be used to control access based on various parameters of request.
/* An example event for authorizer lambda function */
{
"type": "REQUEST",
"methodArn": "arn:aws:execute-api:eu-west-2:xxxx:yyyy/dev/POST/user/bob",
"resource": "/user/{userid}",
"path": "/user/bob",
"httpMethod": "POST",
"queryStringParameters": {},
"multiValueQueryStringParameters": {},
"pathParameters": {
"userid": "bob"
},
"stageVariables": {},
"requestContext": {
"resourceId": "xxxx",
"resourcePath": "/user/{userid}",
"httpMethod": "POST",
"requestTime": "27/Apr/2022:05:24:51 +0000",
"path": "/dev/user/bob",
"accountId": "xxxx",
"protocol": "HTTP/1.1",
"stage": "dev",
"domainPrefix": "xxxxx",
"requestTimeEpoch": 1651037091449,
"requestId": "34d22210-9a2b-4af8-8772-03c03d9b5bec"
}
}
For example, if the users of an API should be restricted to access only the resources in their namespace, it can be done using a resolvable template for allowed resources (endpoints).
Consider the following example role-endpoint mapping:
Role | Allowed Endpoints |
---|---|
REGULAR_USER | /user/{userid}/* |
MANAGER | /user/* |
Here the allowed endpoint template of a REGULAR_USER
can be resolved by replacing the {userid} segment with the actual id of the user, extracted from a trusted source (E.g user id mentioned in the claims of a validated JWT token). This resolved endpoint template can be matched against the path parameters specified the request.
/* Relevant parts of the API gateway request*/
....
"resource": "/user/{userid}",
"path": "/user/bob",
"pathParameters": {
"userid": "bob"
}
....
The authorizer lambda function can now limit the access of a user with REGULAR_USER
role to only allow endpoints with a particular user id.
i.e. A REGULAR_USER
with user id bob can only access /user/bob/*
.
Using Cognito and API Gateway with Lambda authorizer
Cognito groups can be used to group people based on their role and then control access to an API based on it.
- Permissions are based on user groups
- User groups are created in Cognito
- A mapping is created between groups and authorized API endpoints, which is stored in a database
- Custom Lambda authorizer is used to extract group information from authentication token. This Lambda returns a policy which controls the access to the endpoints
RBAC logic coupled with application code
In this approach, the access control takes place within the application code. Common practices include access annotations and auth middleware. The actual implementation depends upon the coding language and frameworks used.
Few examples: