Which Category is your question related to?
GraphQL Schema @auth configuration
Amplify CLI Version
4.23.0
What AWS Services are you utilizing?
AWS AppSync (api), Amazon Cognito (auth), Amazon DynamoDB (storage)
Provide additional details e.g. code snippets
I am trying to share access to a Model with multiple users. I'm doing so through multiple auth rules where the owner is identified by the field owner and other users are given access through ownerField. Here's my schema:
type Channel
@model
@auth(rules: [
{ allow: owner },
{ allow: owner, ownerField: "members", operations: [read] }
])
{
id: ID!
owner: String
members: [String]
}
The problem is the following, I have no idea how to listen for onCreateChannel events for member users. As per documentation:
...the user will only get notified of updates to records for which they are the owner.
The only alternative I found was through Static group authorization. As per docs, when using static group auth:
If you don鈥檛 pass the user in, but are a member of an allowed group, the subscription will notify you of records added.
So for example I could enable read for everyone in the User group/role. But that's obviously not desired.
What would you recommend? Again and to summarize. I have a simple Channel table where the creator has CRUDL and members can only read.
@abdielou
Is members a list type in the jwt? If so are you looking to allow onCreateChannel for all members?
I am having exactly the same problem, using the example from the CLI Directives examples.
type Draft @model
@auth(rules: [
# Defaults to use the "owner" field.
{ allow: owner },
# Authorize the update mutation and both queries.
{ allow: owner, ownerField: "editors", operations: [update, read] }
]) {
id: ID!
title: String!
content: String
owner: String
editors: [String]
}
Test 1: create mutation with no explicit owner should populate owner and editor field with username
Expected result (according to docs) - should populate editors array and owner.
Failed - Only populates owner.
Solution - The documentation should have "create" in the list of editor operations
{ allow: owner, ownerField: "editors", operations: [create, update, read] }
Retest: Pass
Test 2 owner can read Test 1 record
Result: Pass
Test 3 "otheruser" can't read test 1 record
Result: Pass - does not appear in list result
Test 4 Update Test 1 record so editors contains ["usename", "otheruser"] should mean both users can read it
Result: Pass
Test 5 "Username" user gets subscription notifications when subscribing with input:{user: "username", editors:"username"}
Result: Pass
Test 6 "otheruser" gets notifications input:{user: "otheruser", editors:"otheruser"}
Result: FAIL - no notifications
if you try to set user to any other name you get something similar to an authentication error
I tried simplifying the auth rules to make "owner" an array, in the hope that both owners would get notifications. No luck. They could both list and edit but neither got notifications
@auth(rules: [ { allow: owner }, ]) {
owner: [String!]!
At the moment it looks like "owner" has to be a Single string, so only one owner gets notified - unless you can use static groups
Is members a list type in the jwt?
I'm not sure I follow. What do you mean by "in the JWT"? I am not handling the JWT, as this is handled by the autogenerated code. I suppose you are refering to how I configure the @auth directive. I presume this is how one would affect the composition of the JWT. The following is my code:
@auth(rules: [
{ allow: owner },
{ allow: owner, ownerField: "members", operations: [read] }
])
If so are you looking to allow
onCreateChannelfor all members?
@SwaySway Yes that's what I'm trying to accomplish.
WARNING: This is a hackaround
Finally accomplished what I wanted.
https://github.com/abdielou/nextjs-amplify-members-example
Anyone, please take a look and give me your thoughts?
Workflow:
I'll try to explain this in more detail later. 馃槳
Just an update... I was able to modify the DataStore and enabled syncing with my initial use-case.
This is what I needed, a Channel table where ownership was shared across members, and that members would get notified of channelCreate events.
This is how I roughly did it:
Channel and Membership tables, where membership stored members array.membershipID, I was able to implement a custom resolver to validate authorization of members when trying to subscribe to Channel events.Notification where membershipID is included.membershipID.Membership and Notification tables are protected through @auth.The architecture above works for GQL only. The DataStore builds its own Subscriptions at startup, therefore it's impossible to sync through the custom subscriptions required by the previous architecture.
I modified (still working on cleaning it up) the DataStore to allow custom data sync events, in other words, an Observable is sent through Amplify.DataStore configs, which serves as a bridge between custom Subscriptions on the client and the internal SyncEngine. Events are sent into the SyncEngine for local syncing with the local indexed db.
It works! The DataStore is used normally, and custom subscriptions maintain new objects.
I'm still cleaning up and improving the Channel table security as I am only securing subscriptions. Once I'm happier with the code I'll try to pass it around for thoughts.
NOTE: Again, this is a hackaround, exploring possible longer-term solutions.
This is a limitation of appsync where it expects the exact list with the exact order, which is not possible in this case, as the auto generated subscriptions only accept a string. You can read more about it in this thread and I am not sure, if the appsync/amplify team has any ETA on implementing such a feature
https://stackoverflow.com/questions/56822317/appsync-subscribe-to-element-in-array
Considering that this would not be delivered anytime soon, I tried to use another field as the input of the subscription, lets say in the provided directive example:
type Draft @model
@auth(rules: [
# Defaults to use the "owner" field.
{ allow: owner },
# Authorize the update mutation and both queries.
{ allow: owner, ownerField: "editors", operations: [update, read] }
]) {
id: ID!
title: String!
content: String
owner: String
editors: [String]
newField: String!
}
a new subscription is defined as follows:
type Subscription {
onUpdateDraft(newField: String): Draft @aws_subscribe(mutations: ["updateDraft "])
}
Please note that the newField parameter is optional.
This subscription would be fired on update, however, the received response has an empty data inside value while the provider is populated properly.
Any idea why this is happening?
I realised that this is another bug, where amplify does not work according to the documentation provided here:
https://docs.aws.amazon.com/appsync/latest/devguide/real-time-data.html
Subscriptions dont work with optional arguments, I changed it to a required argument and it is working. Just to explain what I have in mind is another table which is responsible for mapping subscriptions to subscribers and vice versa. It returns a subscription key, which is this newly added field, where subscribers can subscribe to.
I'm going to close this. The original post was regarding how to enable members to listen onCreateChannel events. Not possible. This is a limitation of the Subscription. You need to find a way to get clients notified, SNS, a dedicated Notifications model, or any other PubSub where clients can subscribe. The events could be sent by the clients specifying the members or through post functions.
I have implemented a workaround involving custom resolvers and a Notifications table to get the events through GraphQL. All that works if you are just using GQL. If you want to use the DataStore... the story is a bit different because you will not be able to sync. The DataStore only allows for single ownership, so your subscriptions will fail.
This is what I did:
ownerowner and notifierChannel.owner will be sent, but members is also required.owner.SyncEngine through a custom observable.So yeah, original issue is not possible. In the future I'm replacing the Notifications table with a PubSub like SNS.