amplify-js: DataStore with @auth - Sync error subscription failed ... Missing field argument owner
tl;dr Is it necessary to configure DataStore to include owner for syncing when using owner auth? If so, what’s the syntax or can anyone point to a working example?
Which Category is your question related to?
Amplify CLI, DataStore, auth, GraphQL API
Amplify CLI Version
4.6.0
What AWS Services are you utilizing?
Amplify CLI, Datastore, AppSync, Cognito User Pools
Provide additional details e.g. code snippets
Hi all, attempting to add simple owner authorization to this DataStore demo https://github.com/sebsto/amplify-datastore-js-e2e .
Changes to the demo repo
@auth directive was added to schema:
enum PostStatus {
ACTIVE
INACTIVE
}
type Post @model @auth(rules: [{ allow: owner }]) {
id: ID!
title: String!
rating: Int!
status: PostStatus!
}
App.js updated for auth:
export default withAuthenticator(App, true);
The usual amplify init, amplify add auth (Cognito User Pools), npm run amplify-modelgen, npm run amplify-push, possibly other commands in there
Signing up and signing in…
Result
Browser fails to sync records giving the following in the console:
[WARN] 21:10.98 DataStore - Sync error subscription failed Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onCreatePost'"}]}
which is followed after ~20 seconds by:
AWSAppSyncRealTimeProvider.ts:506 Uncaught TypeError: Cannot read property 'observer' of undefined
at AWSAppSyncRealTimeProvider._timeoutStartSubscriptionAck (AWSAppSyncRealTimeProvider.ts:506)
at AWSAppSyncRealTimeProvider.ts:318
Please note
- same errors when commenting out the useEffect containing the
DataStore.observe(Post).subscribe(...)call and clicking buttons to trigger aDataStore.query()instead, so it seems DataStore is trying to set up subscriptions itself - same errors when adding
ownerto the call toDataStore.save()apart from records created in IndexedDB now showingowner - from the AppSync console, querying and mutating Posts as the authenticated user works fine and includes an
ownerfield
So, is it necessary to configure DataStore explicitly to use owner? e.g. an equivalent to API.graphql(graphqlOperation(onUpdateNote, { owner })).subscribe(...) in the GraphQL API? Or should DataStore pick that up automagically and the issue is somewhere else?
(Also wondering, are there any samples/demos/docs using DataStore together with auth yet? I understand it’s early days for DataStore but haven’t found any so far)
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 8
- Comments: 82 (18 by maintainers)
@brene @Ashish-Nanda @undefobj @SwaySway @amhinson @manueliglesias @sammartinez @yuth @iartemiev @elorzafe @UnleashedMind
DataStore
@authdirective combinationsLike I said in my previous comment, I am currently working on a React Native app which utilizes the great power of DataStore to allow users to use the app while being offline. With that being said, just like a lot of other people I am running into issues when trying to use DataStore in conjunction with the
@authdirective.When I first read the Amplify docs, I was under the impression that almost all the documented directives would work in conjunction with the DataStore, but unfortunately this doesn’t seem to be the case. (Could we maybe add a separate directives section to the DataStore documentation?)
Problems start to arise (in the form of failing subscriptions) whenever you try and limit operations, or try and layer multiple different authentication methods.
In an effort to help out the AWS Amplify team, and to speed up fixes for these use cases, I tested a variation of different
@authrules in combination with the DataStore. The outcome can be found below.For the sake of the app we’re working on, which we plan on releasing very soon (fingers crossed), and everyone else wanting to leverage the power of DataStore but requires slightly more complex
@authdirective usage beyond a single user or multiple users that have full access to every operation, hopefully this helps even a little bit.Extra context
Library versions:
These steps were performed with/before each test:
If applicable to schema, the following steps were also performed:
Schema’s
Only 5/14 schema’s work as expected. I am particularly interested in limiting operations for an owner, while allowing the owner to receive updates for all operations, and having IAM authentication in combination with the owner rule. I was unable to get any subscriptions to work when layering an IAM auth rule on top of an owner rule.
Schema 1 (works) #
Generates the following subscriptions:
This schema works as expected.
Schema 2 #
Generates the following subscriptions:
This schema doesn’t work as expected. Subscriptions fail, and result in the following warnings/errors:
Schema 3 #
Generates the following subscriptions:
This schema doesn’t work as expected. Subscriptions fail, and result in the following warnings/errors:
Schema 4 #
Generates the following subscriptions:
This schema doesn’t work as expected. Subscriptions fail, and result in the following warnings/errors:
Schema 5 (works) #
Generates the following subscriptions:
This schema works as expected.
Schema 6 #
Generates the following subscriptions:
This schema doesn’t work as expected. The onDeleteSharedContent and onUpdateSharedContent subscriptions fail. The result is the following warnings/errors:
I guess in this case, the subscriptions failing could be seen as “intended” since the owner doesn’t have the update and delete operations, but I feel like a more common use case would be that the owner can’t run the update and delete mutations, but still has access to the onUpdate and onDelete subscriptions. Maybe introducing something like a “listen” operation, which I saw in a different GitHub issue would be a way to control this behaviour separately?
Schema 7 #
Generates the following subscriptions:
This schema doesn’t work as expected. The onDeleteSharedContent subscription fails, and result in the following warnings/errors:
Same as in Schema 6, the subscription failing could be seen as “intended” since the owner doesn’t have the delete operation, but I feel like a more common use case would be that the owner can’t run the delete mutation, but still has access to the onDelete subscriptions. Maybe introducing something like a “listen” operation, which I saw in a different GitHub issue would be a way to control this behaviour separately?
Schema 8 (works) #
Requires you to pass an idToken instead of an accessToken in Amplify configure:
Generates the following subscriptions:
This schema works as expected.
Schema 9 #
Requires you to pass an idToken instead of an accessToken in Amplify configure:
Generates the following subscriptions:
This schema doesn’t work as expected. The onDeleteSharedContent subscription fails, and result in the following warnings/errors:
Same as in Schema 6 & 7, the subscription failing could be seen as “intended” since the owner doesn’t have the delete operation, but I feel like a more common use case would be that the owner can’t run the delete mutation, but still has access to the onDelete subscriptions. Maybe introducing something like a “listen” operation, which I saw in a different GitHub issue would be a way to control this behaviour separately?
Schema 10 #
Generates the following subscriptions:
This schema doesn’t work as expected. Subscriptions work, but users see other user’s content. Queries properly return only the owner’s content.
Schema 11 #
Similar to example from docs: https://docs.amplify.aws/cli/graphql-transformer/directives#multiple-authorization-rules
Generates the following subscriptions:
This schema doesn’t work as expected. Subscriptions fail, and result in the following warnings/errors:
Schema 12 #
Generates the following subscriptions:
This schema doesn’t work as expected. Subscriptions fail, and result in the following warnings/errors:
Schema 13 (works) #
Based on recent fix: https://github.com/aws-amplify/amplify-cli/pull/4340
Generates the following subscriptions:
This schema works as expected, but the recent fix only seems relevant if you use group auth.
Schema 14 (works) #
Generates the following subscriptions:
This schema works as expected, and is useful if you don’t care that your subscriptions are public.
Thank you @mdoesburg
I just want to post an update here to let you know that we are looking into this, thank you so much for the super thorough breakdown!
@chuckatc We’re actively working on this and expect a fix for this very soon. cc @yuth @elorzafe
@mauerbac Is there a plan or strategy in place to fix this issue? You mentioned here there’s at least an intention to solve this.
I would like to know if there’s an RFC for this. I’d love to know what’s the plan because that can help me get prepared for the fix.
I have already implemented a workaround (on my fork) that involves passing an Observable into the
SyncEngineso that events can be tunneled into it. This is not necessarily an elegant or safe solution, but it’s simple and works. I’ve overcome all sync problems due to failing subscriptions and have been able to implement Multiple authorization rules and keep Offline Support.Reopening cc @sammartinez @manueliglesias @iartemiev
I want to add some insight into a couple of issues here. Thanks to @mdoesburg for the heavy lifting in providing a multitude of scenarios. In this post, I’m going to focus on the Schemas closest to my use case.
Schema#11 has 2 owners,
owner: Stringandeditors: [String]When the
DataStoresubscribes to events, the messages thrown are GraphQL validation errors:The reason for this is the way the
DataStorebuilds the GraphQL subscription. Looking at the DataStore code, it will iterate all items with theownerAuthStrategy, and select the last one.The
ownerAuthRuleslist is obtained from the generated schemasrc/models/schema. A plausible solution to this is changing the order of the rules.This solves the GraphQL Validation Error since now the subscription statement (generated automatically by the DataStore) will compile. We can assume then, that all GQL Validation Errors are caused by the DataStore automatic generation.
Schema#6 and others have a different type of error, it’s an authentication error. NOTE: I am not getting this error when testing on my side. My error is different (gql validation) BUT I’m going to ignore that for the purpose of completing this analysis. I am assuming the one provided by @mdoesburg.
This means that the GQL generated by the DataStore is valid, but the authentication failed on the resolver. This is much less related to the DataStore. But, let’s point out that the DataStore generates a subscription statement that does not include the owner because the
groupAuthStrategyhas prevalence.To look further into this we need to look at the generated VTL
amplify/backend/api/<yourapiname>/build/resolvers/Subscription.onUpdateSharedContent.res.vtl.The first thing it’s going to check is the Static Group Authorization. If the user is an Admin or not.
It will then check the owner passed in the subscription against the username in the JWT.
And finally fails if either of the checks above is successful.
Based on the DataStore logic, no owner is passed in the subscription query, therefore the second check will always fail. Which leaves us back to the Static Group Auth. A possibility then is that the user is not part of the Admin group. Other that, I might be missing something.
I’m still getting an error if I use a custom
ownerFieldin@authdecorator. Basically, with the following GraphQL schema it all works fine - defaultownerfield:but when I set the
ownerFieldto be theidfield:it throws the following exception while DataStore attempts to sync local data to the cloud:
From what I can tell, DataStore assigns a random ID to the local model created (not the owner ID as it’s supposed to), so when trying to sync it with AppSync will get an error due to the constraint on ownerField. This is how I use DataStore to create a new user:
and this is the version of Amplify DataStore I am using in package.json:
Can confirm https://github.com/aws-amplify/amplify-js/issues/6108#issuecomment-647708908
Does NOT work:
Works
@sammartinez Any timeline on this? As @johanbuys states, the docs outlined an example for multiple authorization rules which seems to not be working on DataStore.
The ability to limit certain mutations to an owner, for example delete & update, but still allow other users to subscribe to all changes (create, update, delete), seems like a common use case for a lot of apps.
This is a crucial feature for the React Native app I am working on, which uses DataStore since it needs 100% offline capabilities, and seems to be related to 2 other issues I’ve opened:
I had this error, and it was because I specified the ownerField to be ‘owner’ (i.e. the default) when I removed that it worked fine.
I blacked it out because it was specific to my project but similar to Post (from what I recall). I did
amplify push -yfollowed byamplify codegen modelsafter making changes.I started from a new project/new API with a simple but similar schema and it worked as expected. I will continue experimenting with this and I will let you know if I see this issue again. Thanks for the support.
@alexandprivate this might be of interest to you. Not using DataStore yet… I’m going to try that later.
https://github.com/aws-amplify/amplify-cli/issues/4794#issuecomment-665420716
Seems like requiring the owner field in the subscription would break a LOT of really common usecases. For example, if I want to have a chat app, and the
Messagetype hasowner - createauthz, then how do I have other users get subscriptions to when new messages are posted?Hi there @manueliglesias any good news to share so far?
Thank you so much guys for looking into this again, let us know, so we can update our schemas
I ran into pretty much the same issue with
Sync error subscription failed Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onCreateXXX'. In the end, I managed to work around it by explicitly specifying the owner field in the schema:Also during the debugging I realized I forgot to run
amplify codegen models, so that may also be one of the steps leading to this issue.But I don’t think it’s the only one. I now have for testing two models, one with explicit
ownerand one with implicitownerand when using the implicit/automatic one, I don’t get any errors, but I also don’t get subscription functionality. I need to refresh the page each time for new data to load. The implicit model is defined like this:so the only difference is missing explicit
ownerfield. Yet in code,TestFielddoesn’t support subscriptions (needs a page reload to display new data) andSecondTestFieldworks as it should.AWS libs are as follows:
@mkaschke you should also add
@aws-amplify/datastoreto yourpackage.jsonfile.@mkaschke can you rename the type Subscription from annotated schema. That is overwriting the Subscription generated by CLI