amplify-category-api
amplify-category-api copied to clipboard
Dynamic Group Authentication Support for Custom Queries
Describe the feature you'd like to request
I'm building a multi-tenant application with Gen2. Each of my models has a tenantId attribute on it, and each user belongs to a Group in Cognito with that tenantId name. (I'm using Postgres for my database).
models.User.authorization((allow) => [
allow.groupDefinedIn('tenantId'),
]),
This works great and ensures that the tenancy is enforced without any work on the client side and allows me to easily ensure my queries don't leak data from other tenants.
Now I need to add some custom queries. I wrote a stored procedure in postgres to let me do a fuzzy search for users by multiple attributes, so I want to run the following:
searchUsers: a.query()
.arguments({
lookup: a.string().required()
})
.returns(a.ref("user").array())
.handler(a.handler.inlineSql(
`SELECT * FROM public.search_user(:lookup);`
)).authorization(allow => allow.authenticated()),
But here, the groupDefinedIn method is not supported, so the best I can do is authenticated(). This means that my tenancy is not enforced without passing in the tenantId from the client which can be easily modified (not secure).
Amplify doesn't pass the Cognito auth context to the database either, so I can't modify my stored procedure to use something like WHERE tenant_id = current_setting['appsync.identity_claims_custom_tenantId'].
The workaround for now is to move all of this to a custom Lambda function and manage it all manually, but that means losing out on the super simple way of handling custom queries in Gen 2.
Describe the solution you'd like
There are two possible solutions here:
-
the preferred approach would be to allow custom queries to support the
groupDefinedIn()method. This would let me treat my custom queries the same way I handle the rest of my models and their associated queries. -
alternatively, if the auth context were to be passed to the database, then I could write my own enforcement logic into the database itself using row level security or WHERE clauses. This isn't as elegant, but would still solve the problem.
Describe alternatives you've considered
Right now, I'm writing a Lambda function using the postgres library and writing my custom queries within that. This requires me to rebuild all the connection/auth logic again (that I would otherwise get for free from the data layer). It also introduces another function that only gets called periodically meaning I have to contend with more cold starts affecting response time back to the client.
Additional context
While I'm not certain of the decision making to not include support for groupDefinedIn() for custom queries, the only risk that I see would be if the query does not return the column identified in groupDefinedIn which should result in an error or automatic unauthorized rather than allowing it to pass through freely.
Is this something that you'd be interested in working on?
- [ ] 👋 I may be able to implement this feature request
Would this feature include a breaking change?
- [ ] ⚠️ This feature might incur a breaking change