How to deploy a fine grained authorization model? #281
Description
The application requirements
- Offline first (browser storage ideally IndexedDB, synchronization, conflict detection/resolution)
- Fine grained authorization system (user management)
- Having organization that contains members & teams
- A team have some access to company data (read, write, admin)
- A user inside a team inherits it's right from the team but can be also overwritten
- A instance is attached to a organization
- The team rights for a instance is inherited from the company right and can be overwritten
- (same for user)
There is a sample GraphQL schema:
type User
@model
{
id: ID!
firstName: String!
lastName: String!
avatarUrl: Url
}
type Team
@model
{
id: ID!
name: String!
}
union Entity = User | Team
type Organisation
@model
{
id: ID!
name: String!
}
enum OrganisationRoleEnum {
NO_ACCESS
READ_ACCESS
WRITE_ACCESS
ADMIN_ACCESS
}
type OrganisationRole
@model
@key(fields: ["entityID", "organisationID"])
{
entityID: ID!
entity: Entity! @connection(fields: ["entityID"])
organisationID: ID!
organisation: Organisation! @connection(fields: ["organisationID"])
teamID: ID
team: Team @connection(fields: ["teamID"])
role: OrganisationRoleEnum
}
type ObjectTypeA
@model
{
id: ID!
name: String!
}
type ObjectTypeB
@model
{
id: ID!
name: String!
}
type ObjectTypeC
@model
{
id: ID!
name: String!
}
# Take in consideration that each object types has different configurations and relations
union Instance = ObjectTypeA | ObjectTypeB | ObjectTypeC
enum InstanceRoleEnum {
NO_ACCESS
READ_ACCESS
WRITE_ACCESS
ADMIN_ACCESS
}
type InstanceRole
@model
@key(fields: ["instanceID", "entityID"])
{
instanceID: ID!
instance: Instance! @connection(fields: ["instanceID"])
entityID: ID!
entity: Entity! @connection(fields: ["entityID"])
organisationID: ID
organisation: Organisation @connection(fields: ["organisationID"])
role: InstanceRoleEnum
}
Investigations
From what I've seen in the web we have two worlds, the first one is Apollo Client and the second one is DataStorage.
Apollo Client comes with lot of issues, if you use the out of the box appsync client you are stick to Apollo 2.X and can't use react hook. If you migrates to Apollo 3.X to have hooks support you only takes the two links from appsync (HTTP & WebSocket) but you face to offline capabilities issues.
DataStorage is relatively new but really promissing. It comes with a new way to sync data with the appsync, use IndexedDB as local storage and have a really easy syntax to use.
In terms of backend deployment we have AWS CDK and Amplify CLI.
AWS CDK you can programmatically generates the cloudformation stuff. You go through your folders architecture and you generates Table, AppSync, DataSource, Resolver... (everything).
Amplify CLI come with GraphQL transformations that take the job of generating the stuff for you. A good collection of directives but a very basic authorization system.
The two solution that I really like and would use for our team because it's easier to use would be DataStorage with Amplify CLI because once you setup your stuff you don't need to care about the needed stuff for deployment but I encounter some lack in terms of authorization.
How to deploy a fine grained authorization model?
From what I've seen I can use either a Lambda authorizer or a Pipeline resolver for doing the needed check for the authorization.
The use case is I need to check the access right for each items in a query list, for a desired mutation and provide the uacpdate to only the subscribers that have the right to see the object.
My question is how do I do that with Amplify CLI? can I use the actual @auth
? can I write a custom one? how do I do for the subscriptions? and which of Lambda authorizer or pipeline resolver is the most optimized with appsync?