Owner vs. Group Access Control in AWS Amplify API
This post compares the behavior of owner access control with group access control in the AWS Amplify API.
To add access control to your API, you add access control annotations to your GraphQL schema in amplify\backend\api\APINAME\schema.graphql
Owner access control has the form @auth(rules: [{allow: owner, ...owner access control settings...}])
Group access control has the form @auth(rules: [{allow: groups, ...group access control settings...}])
AWS Amplify then takes your access control specification and generates resolvers that contain the access control code to enforce your specification.
There are good explanations of how this works in practice in the AWS Amplify docs, but I wanted to think about the owner and group access control more abstractly. By doing that, you can think of owner and group access control as abstract foundations on which you can build a whole spectrum of access control policies, like multi-tenancy. As such, it’s useful to understand the similarities and differences between owner and group access control before starting to build.
Owner access control
Owner access control has the following form @auth(rules: [{allow: owner, ownerField: "fieldName", identityClaim: "claimName"}])
As specified in the AWS Amplify documentation, “each object has an ownerField
(by default ‘owner
’) that stores ownership information.” To specify that a single user should have access, use an ownerField
of type String
. To specify that multiple users should have access, use an ownerField
of type [String]
IThe identityClaim
specifies a field, assumed to contain a scalar value like a string, in the user’s access token. By default this is the username
field in the access token.
To think of it abstractly, if the value in the
identityClaim
field of the user’s access token matches any of the values in theownerField
field of the object to be accessed, the user is authorized.
Group access control
Group access control has the following form @auth(rules: [{allow: groups, groupsField: "fieldName", groupClaim: "claimName"}])
As specified in the documentation, “With dynamic group authorization, each record contains an attribute specifying what groups should be able to access it. Use the groupsField
argument to specify which attribute in the underlying data store holds this group information. To specify that a single group should have access, use a field of type String
. To specify that multiple groups should have access, use a field of type [String]
”
The groupClaim
specifies a field, assumed to be a list of values, in the user’s access token. By default this is the cognito:groups
list in the access token, which contains the list of AWS Cognito groups the user belongs to.
A subtlety is that that AWS Cognito custom attributes can only be a string or a number (not a list of strings). So if you use a custom attribute as your groupClaim
, the generated access control code will first parse the string into a list using the $util.parseJson
function. So the string in your custom attribute should look like '["value1", "value2",…]'
To think of it abstractly, if any of the values in the
groupClaim
field of the user’s access token match any of the values in thegroupsField
field of the object to be accessed, the user is authorized.
Queries, and Update & Delete Mutations
For GraphQL queries (getX
and listX
), and update & delete mutations, the primary difference between owner and group access control is that the user is assumed to have a scalar value in identityClaim
(a user can only have a single unique identifier), but a list of values in groupClaim
(a user can be a member of multiple groups).
As such, for these operations, you can think of group access control as a generalization of owner access control, because you can emulate owner access control in group access control by converting the value in
identityClaim
to a single element list.
Create Mutations
For GraphQL create mutations, a major difference is that, in owner access control, if a create mutation operation does not specify a value for the ownerField
in the object it is creating, the value from the identityClaim
field in the access token (by default the username
), is placed in the ownerField
of the created object, and authorization is automatically approved.
There’s no analogous system for group access control because a groupClaim
is a list of values. It wouldn’t be clear which of the elements of the list should be automatically placed in the groupsField
if the user didn’t explicitly specify a value.
Summary
So when choosing between owner and group access control to build your final access control system on, keep the following two things in mind. Group access control is more generalized, because its claim can contain a list of values, not just a single value as in owner access control. But owner access control allows you to specify a default value during object creation.