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
DataStore.observe(Post).subscribe(...)
call and clicking buttons to trigger a DataStore.query()
instead, so it seems DataStore is trying to set up subscriptions itselfowner
to the call to DataStore.save()
apart from records created in IndexedDB now showing owner
owner
fieldSo, 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)
@chuckatc We're actively working on this and expect a fix for this very soon.
cc @yuth @elorzafe
I'm hitting this too, or at least something similar, take the following slightly extended example,
Type Task
@model
@auth(rules: [
{allow: groups, groups: ["SuperUser"], queries: [get, list], mutations: [create, update, delete]},
{allow: owner, queries: [get, list], mutations: [create]}
])
{
id: ID!
title: String!
description: String
status: String
}
I'd expect an owner to be able to subscribe to onCreateTask, and get only their 'Tasks', and a user in the SuperUser group to be able to subscribe to _all_ onCreateTask events from all users. As it is today the owner is a required argument to the generated subscription, so when a member of the SuperUser group subscribes with their user as owner they only see their create events not all create events.
EDIT:
So playing around a bit more I changed things to the following and it is now working as expected.
Type Task
@model
@auth(rules: [
{allow: groups, groups: ["SuperUser"]},
{allow: owner, queries: [get, list], mutations: [create]}
])
{
id: ID!
title: String!
description: String
status: String
}
I also had to change the auto-generated subscription to remove the argument ($owner: String!)
.
I suspect you can discount my comment, but there appears to be something odd going on here. Let me know if there are some combinations you need me to test to track this down further.
I think I'm hitting something similar to my app. Setup is pretty much the same as the original author, Javascript client, amplify generated backend and models.
Schema is this:
type Timeline
@model
@searchable
@auth(
rules: [
{ allow: groups, groups: ["SuperUser"] }
{ allow: owner }
]
) {
id: ID!
name: String!
startDate: AWSDate!
endDate: AWSDate!
investment401k: [Investment401k]
@connection(name: "TimelineInvestment401ks")
income: [Income] @connection(name: "TimelineIncomes")
}
type Income
@model
@searchable
@auth(
rules: [
{ allow: groups, groups: ["SuperUser"] }
{ allow: owner }
]
) {
id: ID!
startDate: AWSDate!
endDate: AWSDate
rate: Float!
object: AWSJSON
timeline: Timeline! @connection(name: "TimelineIncomes")
}
type Investment401k
@model
@searchable
@auth(
rules: [
{ allow: groups, groups: ["SuperUser"] }
{ allow: owner }
]
) {
id: ID!
object: AWSJSON
timeline: Timeline! @connection(name: "TimelineInvestment401ks")
}
type UserData
@model
@searchable
@auth(
rules: [
{ allow: groups, groups: ["SuperUser"] }
{ allow: owner }
]
) {
id: ID!
birthdate: AWSDate!
retirementdate: AWSDate!
profileImageUrl: String
profileImageUncroppedUrl: String
profileImageManipulations: AWSJSON
}
aws-exports.js, which is generated by Amplify, is this (test environment, nothing production):
// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
const awsmobile = {
"aws_project_region": "us-east-1",
"aws_cognito_identity_pool_id": "us-east-1:85e7460b-feab-4f21-87b7-a9a265125c77",
"aws_cognito_region": "us-east-1",
"aws_user_pools_id": "us-east-1_txxH6nvSj",
"aws_user_pools_web_client_id": "7t9mnj8a2pqfrju2e0tjh40ka4",
"oauth": {
"domain": "modelmymoney655c1605-655c1605-localdev.auth.us-east-1.amazoncognito.com",
"scope": [
"phone",
"email",
"openid",
"profile",
"aws.cognito.signin.user.admin"
],
"redirectSignIn": "http://localhost/auth/signin/",
"redirectSignOut": "http://localhost/auth/signout/",
"responseType": "code"
},
"federationTarget": "COGNITO_USER_POOLS",
"aws_appsync_graphqlEndpoint": "https://aitvi3puvfcwfkhqmhmsiw6i3q.appsync-api.us-east-1.amazonaws.com/graphql",
"aws_appsync_region": "us-east-1",
"aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
"aws_appsync_apiKey": "da2-g47wpfle4rhzrotlqnfu6cgew4",
"aws_user_files_s3_bucket": "model-my-money947365a9c1734517af86f8ba26db26a7money-localdev",
"aws_user_files_s3_bucket_region": "us-east-1"
};
export default awsmobile;
Relevant lines from main.js
import Amplify from 'aws-amplify';
import awsconfig from '@/aws-exports';
Amplify.configure(awsconfig);
And the generated schema.js in the /models/ folder:
export const schema = {
"models": {
"Timeline": {
"syncable": true,
"name": "Timeline",
"pluralName": "Timelines",
"attributes": [
{
"type": "model",
"properties": {}
},
{
"type": "searchable",
"properties": {}
},
{
"type": "auth",
"properties": {
"rules": [
{
"groupClaim": "cognito:groups",
"provider": "userPools",
"allow": "groups",
"groups": [
"SuperUser"
],
"operations": [
"create",
"update",
"delete"
]
},
{
"provider": "userPools",
"ownerField": "owner",
"allow": "owner",
"identityClaim": "cognito:username",
"operations": [
"create",
"update",
"delete"
]
}
]
}
}
],
"fields": {
"id": {
"name": "id",
"isArray": false,
"type": "ID",
"isRequired": true,
"attributes": []
},
"name": {
"name": "name",
"isArray": false,
"type": "string",
"isRequired": true,
"attributes": []
},
"startDate": {
"name": "startDate",
"isArray": false,
"type": "AWSDate",
"isRequired": true,
"attributes": []
},
"endDate": {
"name": "endDate",
"isArray": false,
"type": "AWSDate",
"isRequired": true,
"attributes": []
},
"investment401k": {
"name": "investment401k",
"isArray": true,
"type": {
"model": "Investment401k"
},
"isRequired": false,
"attributes": [],
"association": {
"connectionType": "HAS_MANY",
"associatedWith": "timeline"
}
},
"income": {
"name": "income",
"isArray": true,
"type": {
"model": "Income"
},
"isRequired": false,
"attributes": [],
"association": {
"connectionType": "HAS_MANY",
"associatedWith": "timeline"
}
}
}
},
"Investment401k": {
"syncable": true,
"name": "Investment401k",
"pluralName": "Investment401ks",
"attributes": [
{
"type": "model",
"properties": {}
},
{
"type": "searchable",
"properties": {}
},
{
"type": "auth",
"properties": {
"rules": [
{
"groupClaim": "cognito:groups",
"provider": "userPools",
"allow": "groups",
"groups": [
"SuperUser"
],
"operations": [
"create",
"update",
"delete"
]
},
{
"provider": "userPools",
"ownerField": "owner",
"allow": "owner",
"identityClaim": "cognito:username",
"operations": [
"create",
"update",
"delete"
]
}
]
}
}
],
"fields": {
"id": {
"name": "id",
"isArray": false,
"type": "ID",
"isRequired": true,
"attributes": []
},
"object": {
"name": "object",
"isArray": false,
"type": "AWSJSON",
"isRequired": false,
"attributes": []
},
"timeline": {
"name": "timeline",
"isArray": false,
"type": {
"model": "Timeline"
},
"isRequired": true,
"attributes": [],
"association": {
"connectionType": "BELONGS_TO",
"targetName": "investment401kTimelineId"
}
}
}
},
"Income": {
"syncable": true,
"name": "Income",
"pluralName": "Incomes",
"attributes": [
{
"type": "model",
"properties": {}
},
{
"type": "searchable",
"properties": {}
},
{
"type": "auth",
"properties": {
"rules": [
{
"groupClaim": "cognito:groups",
"provider": "userPools",
"allow": "groups",
"groups": [
"SuperUser"
],
"operations": [
"create",
"update",
"delete"
]
},
{
"provider": "userPools",
"ownerField": "owner",
"allow": "owner",
"identityClaim": "cognito:username",
"operations": [
"create",
"update",
"delete"
]
}
]
}
}
],
"fields": {
"id": {
"name": "id",
"isArray": false,
"type": "ID",
"isRequired": true,
"attributes": []
},
"startDate": {
"name": "startDate",
"isArray": false,
"type": "AWSDate",
"isRequired": true,
"attributes": []
},
"endDate": {
"name": "endDate",
"isArray": false,
"type": "AWSDate",
"isRequired": false,
"attributes": []
},
"rate": {
"name": "rate",
"isArray": false,
"type": "Float",
"isRequired": true,
"attributes": []
},
"object": {
"name": "object",
"isArray": false,
"type": "AWSJSON",
"isRequired": false,
"attributes": []
},
"timeline": {
"name": "timeline",
"isArray": false,
"type": {
"model": "Timeline"
},
"isRequired": true,
"attributes": [],
"association": {
"connectionType": "BELONGS_TO",
"targetName": "incomeTimelineId"
}
}
}
},
"UserData": {
"syncable": true,
"name": "UserData",
"pluralName": "UserData",
"attributes": [
{
"type": "model",
"properties": {}
},
{
"type": "searchable",
"properties": {}
},
{
"type": "auth",
"properties": {
"rules": [
{
"groupClaim": "cognito:groups",
"provider": "userPools",
"allow": "groups",
"groups": [
"SuperUser"
],
"operations": [
"create",
"update",
"delete"
]
},
{
"provider": "userPools",
"ownerField": "owner",
"allow": "owner",
"identityClaim": "cognito:username",
"operations": [
"create",
"update",
"delete"
]
}
]
}
}
],
"fields": {
"id": {
"name": "id",
"isArray": false,
"type": "ID",
"isRequired": true,
"attributes": []
},
"birthdate": {
"name": "birthdate",
"isArray": false,
"type": "AWSDate",
"isRequired": true,
"attributes": []
},
"retirementdate": {
"name": "retirementdate",
"isArray": false,
"type": "AWSDate",
"isRequired": true,
"attributes": []
},
"profileImageUrl": {
"name": "profileImageUrl",
"isArray": false,
"type": "string",
"isRequired": false,
"attributes": []
},
"profileImageUncroppedUrl": {
"name": "profileImageUncroppedUrl",
"isArray": false,
"type": "string",
"isRequired": false,
"attributes": []
},
"profileImageManipulations": {
"name": "profileImageManipulations",
"isArray": false,
"type": "AWSJSON",
"isRequired": false,
"attributes": []
}
}
}
},
"enums": {},
"version": "c3d791373597b9b143173b56a57ae474"
};
Not much to add - same as the others. Fix or a workaround would be fantastic as we're in production with no path forward.
@dbhagen @chuckatc @dconlon-monigo @CodySwannGT the fix for the library is already published on @latest tag on npm.
@elorzafe I was getting the same problem as @chuckatc, just updated to @aws-amplify/datastore
version 1.04 and now the sync is not working at all anymore.
I set log level to DEBUG and on 1.0.3. I can see it's doing the sync, but on 1.0.4. there is nothing logged for it.
Do we need to configure the sync?
same here!
@ashteya @mkaschke can you share your package.json and schema.graphql files?
@elorzafe sure!
package.json
{
"name": "cognito-amplify-custom-auth",
"version": "0.0.1",
"description": "TBD",
"main": "index.js",
"repository": "",
"private": true,
"author": "mkaschke",
"license": "ISC",
"scripts": {
"postinstall": "cp -R node_modules/semantic-ui-css/semantic.min.css node_modules/semantic-ui-css/themes style",
"start": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js",
"test": "mocha --compilers js:babel-core/register --require ./test/test_helper.js --recursive ./test",
"test:watch": "npm run test -- --watch",
"build": "webpack --progress -p"
},
"devDependencies": {
"@babel/core": "^7.8.0",
"@babel/plugin-proposal-class-properties": "^7.8.0",
"@babel/plugin-transform-runtime": "^7.8.0",
"@babel/preset-env": "^7.8.2",
"@babel/preset-react": "^7.8.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"chai": "^4.2.0",
"css-loader": "^3.4.2",
"eslint": "^5.9.0",
"eslint-config-standard": "^12.0.0",
"eslint-config-standard-react": "^7.0.2",
"eslint-plugin-import": "^2.20.0",
"eslint-plugin-node": "^8.0.0",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-react": "^7.17.0",
"eslint-plugin-standard": "^4.0.0",
"jsdom": "^13.0.0",
"json-loader": "^0.5.7",
"mocha": "^5.2.0",
"react-dates": "^21.5.1",
"webpack": "^4.41.5",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.10.1"
},
"dependencies": {
"amplify-cli": "^1.0.0",
"aws-amplify": "^1.3.3",
"aws-amplify-react": "^2.6.3",
"aws-sdk": "^2.601.0",
"babel-polyfill": "^6.26.0",
"history": "^4.10.1",
"moment": "^2.24.0",
"prop-types": "^15.7.2",
"react": "^16.12.0",
"react-addons-test-utils": "^15.6.2",
"react-dom": "^16.12.0",
"react-redux": "^7.1.3",
"react-router": "^5.0.0",
"react-router-dom": "^5.0.0",
"redux": "^4.0.5",
"redux-form": "^8.2.3",
"redux-promise": "^0.6.0",
"redux-thunk": "^2.3.0",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^0.86.0",
"style-loader": "^0.23.1"
}
}
schema.graphql
type vehicle @auth(rules: [{ allow: owner, operations: [read, update] }]) @model @searchable {
id: ID!
location: String
city: String
comment: String
startDate: String
endDate: String
creator: String
}
type Subscription @auth(rules: [{allow: owner}]) @model @searchable {
id: ID!
location: String
city: String
status: String
}
@mkaschke can you rename the type Subscription from annotated schema. That is overwriting the Subscription generated by CLI
@mkaschke you should also add @aws-amplify/datastore
to your package.json
file.
@elorzafe thanks very much for the attention on this.
I'll be able to retry the modified demo with @ latest later next week (currently on the road).
@chuckatc @mkaschke Please feel free to re-open/comment in this thread if you still facing issues
I'm still getting an error if I use a custom ownerField
in @auth
decorator. Basically, with the following GraphQL schema it all works fine - default owner
field:
type User @model @auth(rules: [{ allow: owner }]) {
id: ID!
name: String!
email: AWSEmail!
owner: String # required for subscriptions
}
but when I set the ownerField
to be the id
field:
type User @model @auth(rules: [{ allow: owner, ownerField: "id" }]) {
id: ID!
name: String!
email: AWSEmail!
}
it throws the following exception while DataStore attempts to sync local data to the cloud:
{
"errors":[
{
"path":["createUser"],
"data":null,
"errorType":"Unauthorized",
...
"message":"Not Authorized to access createUser on type User"
}
]
}
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:
const init = async () => {
const authenticatedUser = await Auth.currentAuthenticatedUser();
const user = await DataStore.query(User, authenticatedUser.username);
if (!user) {
const { name, email } = authenticatedUser.attributes;
await DataStore.save(new User({ name, email }));
}
};
and this is the version of Amplify DataStore I am using in package.json:
"@aws-amplify/datastore": "^1.0.4",
@alex-vladut you are correct, DataStore generate random ids internally.
Using id
as ownerField
is currently not supported. What is the use case you are trying to accomplish?
Thanks for reply @elorzafe. At the moment I am mostly exploring DataStore in order to understand how it works and if I'll be able to use it in a prod environment. Here I try to implement a simple chat app making use of DataStore, and the specific use case listed there is to create a new User item whenever a user signs up for the first time. The item/entity is required to store some user specific settings as well as to link a user to their chat rooms and messages.
Basically I wanted to follow the tutorial provided by Nader Dabit and port it over to Amplify DataStore:
https://dev.to/dabit3/building-chatt---a-real-time-multi-user-graphql-chat-app-3jik
@elorzafe , pardon the wait, was stuck on aws-amplify/amplify-cli#3300 . Now unblocked, so will test original project soonishly.
fwiw, this recent DataStore demo from Ed Lima uses @auth(rules: [{ allow: owner }])
the same way so will try that as well.
Please feel free to comment in the thread if auth does not work with DataStore
@alex-vladut is correct. You save my life!!
Hi ! I've been struggling with this issue for a while
"@aws-amplify/datastore": "^2.0.8"
CLI : 4.18.0
type Session
@auth(
rules: [
{
allow: owner
}
]
)
@model {
id: ID!
owner: String
title: String
}
From AWS AppSync RealTime (background operation for DataStore sync)
Validation error of type MissingFieldArgument: Missing field argument owner @ 'onCreateSession'"
I tried many things, custom resolvers... removing the mandatory owner parameter from subscription... but it seems like the other previous issues, that DataStore is not sending the owner field in the subscription
The subscriptions sent by AppSync for DataStore being the following :
"subscription operation {
onCreateSession {
id
owner
title
_version
_lastChangedAt
_deleted
}
}
"
I've read on the Amplify Graph API doc (here) that
You don’t need to pass the user as an argument in the subscription request, since the resolver will instead check the contents of your JWT token.
So I also tried manually adding the access token in the header
Amplify.configure(awsconfig);
Amplify.configure({
graphql_headers: async () => {
const session = await Auth.currentSession();
return {
Authorization: session.getAccessToken().getJwtToken()
};
}
}
});
But the error is still there, so I don't really know what to do... Did I miss something somewhere (I digged into all the related issues here, and tried all the workaround without success)
@tomheno The subscription should receive owner as argument, something like:
subscription operation($owner: String!) {
onCreateSession(owner: $owner) {
id
owner
title
_version
_lastChangedAt
_deleted
}
}
How did you generate those codes?
@richardfan1126 Got it from the console, Amplify Logger is logging at DEBUG level, see the following screenshot :
There's also the same error with onUpdateSession & onDeleteSession subscriptions, that are run by DataStore in background, those messages occures on app start.
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:
type SecondTestField
@auth(
rules: [
{
allow: owner,
ownerField: "owner"
}
]
)
@model {
id: ID!
title: String!
description: String
owner: String
}
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 owner
and one with implicit owner
and 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:
type TestField
@auth(
rules: [
{
allow: owner
}
]
)
@model {
id: ID!
title: String!
description: String
}
so the only difference is missing explicit owner
field. Yet in code, TestField
doesn't support subscriptions (needs a page reload to display new data) and SecondTestField
works as it should.
AWS libs are as follows:
"@aws-amplify/api": "^3.1.9",
"@aws-amplify/datastore": "^2.0.10",
"@aws-amplify/pubsub": "^3.0.10",
"@aws-amplify/ui-react": "^0.2.5",
"aws-amplify": "^3.0.10",
Hi I am having similar issues.
One @auth rule works:
type Task @model
@auth(rules: [
{ allow: owner, ownerField: "owner" }
]) {
id: ID!
title: String!
owner: String
}
But when I want to add a second auth rule
type Task @model
@auth(rules: [
{ allow: owner, ownerField: "owner" },
{ allow: owner, ownerField: "assignee", operations: [update, read] }
]) {
id: ID!
title: String!
owner: String
assignee: String
}
with the following:
[WARN] 40:44.603 DataStore - Sync error subscription failed Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onUpdateTask'"}]}
And does not sync to cloud, local changes are working though.
"dependencies": {
"@aws-amplify/core": "^3.2.6",
"@aws-amplify/datastore": "^2.0.10",
"@aws-amplify/ui-react": "^0.2.5",
I have the same issue.
Using
"@aws-amplify/api": "^3.1.10",
"@aws-amplify/auth": "^3.2.7",
"@aws-amplify/core": "^3.2.7",
"@aws-amplify/datastore": "^2.1.0",
I have tried the various workarounds found here but it still does not work.
@chuckatc mentioned this post here where it seems to work, but I noticed that the author is using npx amplify-app
to init his project, so he does not add an API with amplify add api
.
I am in the case where I had an API before setting up DataStore, so I used the cli to update my API to use DataStore.
@veproza @tomheno In which case are you? Did you init your project with npx amplify-app
or did you update your existing API tu use DataStore?
I have similar error.
Right after authenticate user, I'm trying to perform DataStore.query, and get undefined.
But after waiting a few seconds and reload applications query successfully returns data.
We have similar issues, all models are marked with @auth
aws-exports
aws-exports
Most of our configuration are manual.
Tried amplify api update
.
Also tried DataStore.configure(), which give other error DataStore - Sync error subscription failed Subscribe only available for AWS AppSync endpoint
.
I have the same issue.
Using
"@aws-amplify/api": "^3.1.10", "@aws-amplify/auth": "^3.2.7", "@aws-amplify/core": "^3.2.7", "@aws-amplify/datastore": "^2.1.0",
I have tried the various workarounds found here but it still does not work.
@chuckatc mentioned this post here where it seems to work, but I noticed that the author is using
npx amplify-app
to init his project, so he does not add an API withamplify add api
.I am in the case where I had an API before setting up DataStore, so I used the cli to update my API to use DataStore.
@veproza @tomheno In which case are you? Did you init your project with
npx amplify-app
or did you update your existing API tu use DataStore?
I made the test and it has nothing to do with whether you started the project using npx amplify-app
or you updated an existing API.
I still have this issue in my React Native app, I created a new issue as this one is closed.
I have a similar issue with
type Quiz
@model
@auth(
rules: [
{ allow: public, provider: iam, operations: [read] }
{ allow: owner }
]
) {
id: ID!
title: String!
seconds: Int!
currentQuestion: String
questionOrder: String
started: Boolean
questionTime: Int
view: Int
owner: String!
}
[WARN] 34:16.791 DataStore - Sync error – "Connection failed: {\"errors\":[{\"message\":\"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onUpdateQuiz'\"}]}"
"@aws-amplify/analytics": "^3.1.15",
"@aws-amplify/api": "^3.1.15",
"@aws-amplify/auth": "^3.2.12",
"@aws-amplify/core": "^3.3.2",
"@aws-amplify/datastore": "^2.2.2",
"@aws-amplify/interactions": "^3.1.15",
"@aws-amplify/predictions": "^3.1.15",
"@aws-amplify/storage": "^3.2.5",
"@aws-amplify/ui": "^2.0.2",
"@aws-amplify/ui-react": "^0.2.8",
"@aws-amplify/xr": "^2.1.15",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"array-move": "^2.2.1",
"aws-amplify": "^3.0.13",
"aws-amplify-react": "^4.1.12",
Same issue here
This don't work
@auth(
rules: [
{ allow: owner, ownerField: "owner" }
{ allow: owner, ownerField: "tecs", operations: [read, update] }
]
) {...
But only one rule works perfectly
@auth(
rules: [
{ allow: owner, ownerField: "owner" }
]
) {
"@aws-amplify/datastore": "^2.2.3",
"@aws-amplify/datastore": "^2.0.5",
Hello @alexandprivate is there missing comma in auth array?
Please check with comma, if it works let us know.
Hello @alexandprivate is there missing comma in auth array?
Please check with comma, if it works let us know.
Hi there, well yes and no, prettier actually removed the comma when I save the doc, so I don't think that's the issue, also the model is generated as expected but simply doesn't work, it keeps showing the missing owner field in subscription operation
Ok guys lets see if we can solve this
Hi I am having similar issues.
One @auth rule works:
type Task @model @auth(rules: [ { allow: owner, ownerField: "owner" } ]) { id: ID! title: String! owner: String }
But when I want to add a second auth rule
type Task @model @auth(rules: [ { allow: owner, ownerField: "owner" }, { allow: owner, ownerField: "assignee", operations: [update, read] } ]) { id: ID! title: String! owner: String assignee: String }
with the following:
[WARN] 40:44.603 DataStore - Sync error subscription failed Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onUpdateTask'"}]}
And does not sync to cloud, local changes are working though.
"dependencies": { "@aws-amplify/core": "^3.2.6", "@aws-amplify/datastore": "^2.0.10", "@aws-amplify/ui-react": "^0.2.5",
Hi there @johanbuys any updates on this from your side?
Hi I am having similar issues.
One @auth rule works:type Task @model @auth(rules: [ { allow: owner, ownerField: "owner" } ]) { id: ID! title: String! owner: String }
But when I want to add a second auth rule
type Task @model @auth(rules: [ { allow: owner, ownerField: "owner" }, { allow: owner, ownerField: "assignee", operations: [update, read] } ]) { id: ID! title: String! owner: String assignee: String }
with the following:
[WARN] 40:44.603 DataStore - Sync error subscription failed Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onUpdateTask'"}]}
And does not sync to cloud, local changes are working though."dependencies": { "@aws-amplify/core": "^3.2.6", "@aws-amplify/datastore": "^2.0.10", "@aws-amplify/ui-react": "^0.2.5",
Hi there @johanbuys any updates on this from your side?
Hi, no not really.
I still have the issue. It looks like, If I change around the 2 rules the error changes to match the first field.
This was core to our use case, I did not want to hack something together to make this work so I haven't looked into this for a while. I am monitoring this thread, but I had to refactor my code, not using Datastore for now.
Hopefully these issues can be fixed I was really eager to use this, but for now it's a showstopper.
Hi I am having similar issues.
One @auth rule works:type Task @model @auth(rules: [ { allow: owner, ownerField: "owner" } ]) { id: ID! title: String! owner: String }
But when I want to add a second auth rule
type Task @model @auth(rules: [ { allow: owner, ownerField: "owner" }, { allow: owner, ownerField: "assignee", operations: [update, read] } ]) { id: ID! title: String! owner: String assignee: String }
with the following:
[WARN] 40:44.603 DataStore - Sync error subscription failed Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onUpdateTask'"}]}
And does not sync to cloud, local changes are working though."dependencies": { "@aws-amplify/core": "^3.2.6", "@aws-amplify/datastore": "^2.0.10", "@aws-amplify/ui-react": "^0.2.5",
Hi there @johanbuys any updates on this from your side?
Hi, no not really.
I still have the issue. It looks like, If I change around the 2 rules the error changes to match the first field.
This was core to our use case, I did not want to hack something together to make this work so I haven't looked into this for a while. I am monitoring this thread, but I had to refactor my code, not using Datastore for now.
Hopefully these issues can be fixed I was really eager to use this, but for now it's a showstopper.
Oh! Sad to hear it, I would like to keep DataStore but implicates to hacking the request buy user and basically remove the @auth from the affected model, let's hope this could be solved sooner than expected.
@elorzafe any updates on this so far?
Hi everybody, I've been having this issue. I'm noticing that it basically only happens when you limit certain operations. Having only operations: ["read"] for me, was producing errors for "onCreate{model}", "onUpdate{model}", and on "onDelete{model}". When I added "update" and "delete" operations, only the error for "onCreate{model}" appeared.
It seems that the Amplify CLI is generating more than it needs to in subscriptions.graphql and maybe elsewhere. I haven't looked into this more as I just found this out, but it's worth trying if you think it helps! I'll comment again if I can figure out more.
edit: In case you're wondering, my model was set to only have operations: ["read"] overall despite my project needing to track changes in real time. A few of the items also had "update", but apparently the object overall needs to adopt all operations for DataStore to work.
Hi everybody, I've been having this issue. I'm noticing that it basically only happens when you limit certain operations. Having only operations: ["read"] for me, was producing errors for "onCreate{model}", "onUpdate{model}", and on "onDelete{model}". When I added "update" and "delete" operations, only the error for "onCreate{model}" appeared.
It seems that the Amplify CLI is generating more than it needs to in subscriptions.graphql and maybe elsewhere. I haven't looked into this more as I just found this out, but it's worth trying if you think it helps! I'll comment again if I can figure out more.
edit: In case you're wondering, my model was set to only have operations: ["read"] overall despite my project needing to track changes in real time. A few of the items also had "update", but apparently the object overall needs to adopt all operations for DataStore to work.
This is also what I have found. See this https://docs.amplify.aws/cli/graphql-transformer/directives#multiple-authorization-rules
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]
}
And this translates to:
Draft that stores unfinished posts for a blog. You might want to allow the Draft’s owner to create, update, delete, and read Draft objects. However, you might also want the Draft’s editors to be able to update and read Draft objects.
This is the exact use case I was trying to accomplish and it did not work.
@yuth @elorzafe @undefobj
Can you re-open this issue? It is still not solved in the last relase!
Reopening cc @sammartinez @manueliglesias @iartemiev
Thanks @undefobj, Im going to transfer this to the Amplify JS Repo so we can dig into it
Thank you so much guys for looking into this again, let us know, so we can update our schemas
@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:
@brene @Ashish-Nanda @undefobj @SwaySway @amhinson @manueliglesias @sammartinez @yuth @iartemiev @elorzafe @UnleashedMind
@auth
directive 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 @auth
directive.
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 @auth
rules 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 @auth
directive usage beyond a single user or multiple users that have full access to every operation, hopefully this helps even a little bit.
Library versions:
These steps were performed with/before each test:
If applicable to schema, the following steps were also performed:
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.
type SharedContent
@model
@auth(rules: [
{ allow: owner }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent(owner: String!): SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent(owner: String!): SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent(owner: String!): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema works as expected.
type SharedContent
@model
@auth(rules: [
{ allow: owner, operations: [create, update, delete] }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent: SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent: SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent: SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema doesn't work as expected. Subscriptions fail, and result in the following warnings/errors:
[WARN] 38:14.669 DataStore - Connection failed: {"errors":[{"message":"Validation error of type UnknownArgument: Unknown field argument owner @ 'onDeleteSharedContent'"}]}
[WARN] 38:14.756 DataStore - Connection failed: {"errors":[{"message":"Validation error of type UnknownArgument: Unknown field argument owner @ 'onUpdateSharedContent'"}]}
[WARN] 38:14.802 DataStore - Connection failed: {"errors":[{"message":"Validation error of type UnknownArgument: Unknown field argument owner @ 'onCreateSharedContent'"}]}
type SharedContent
@model
@auth(rules: [
{ allow: owner, operations: [create, update, delete] },
{ allow: private, operations: [read] }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent: SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent: SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent: SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema doesn't work as expected. Subscriptions fail, and result in the following warnings/errors:
[WARN] 53:08.331 DataStore - Connection failed: {"errors":[{"message":"Validation error of type UnknownArgument: Unknown field argument owner @ 'onCreateSharedContent'"}]}
[WARN] 53:08.358 DataStore - Connection failed: {"errors":[{"message":"Validation error of type UnknownArgument: Unknown field argument owner @ 'onDeleteSharedContent'"}]}
[WARN] 53:08.413 DataStore - Connection failed: {"errors":[{"message":"Validation error of type UnknownArgument: Unknown field argument owner @ 'onUpdateSharedContent'"}]}
type SharedContent
@model
@auth(rules: [
{ allow: owner },
{ allow: private, provider: iam }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent(owner: String!): SharedContent @aws_subscribe(mutations: ["createSharedContent"]) @aws_iam @aws_cognito_user_pools
onUpdateSharedContent(owner: String!): SharedContent @aws_subscribe(mutations: ["updateSharedContent"]) @aws_iam @aws_cognito_user_pools
onDeleteSharedContent(owner: String!): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"]) @aws_iam @aws_cognito_user_pools
This schema doesn't work as expected. Subscriptions fail, and result in the following warnings/errors:
[WARN] 26:54.58 DataStore - Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onDeleteSharedContent'"}]}
[WARN] 26:54.97 DataStore - Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onCreateSharedContent'"}]}
[WARN] 26:54.156 DataStore - Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onUpdateSharedContent'"}]}
type SharedContent
@model
@auth(rules: [
{ allow: owner },
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema works as expected.
type SharedContent
@model
@auth(rules: [
{ allow: owner, operations: [create, read] },
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema doesn't work as expected. The onDeleteSharedContent and onUpdateSharedContent subscriptions fail. The result is the following warnings/errors:
[WARN] 33:56.520 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onDeleteSharedContent on type Subscription"}]}
[WARN] 33:56.553 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onUpdateSharedContent on type Subscription"}]}
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?
type SharedContent
@model
@auth(rules: [
{ allow: owner, operations: [create, read, update] },
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema doesn't work as expected. The onDeleteSharedContent subscription fails, and result in the following warnings/errors:
[WARN] 51:55.649 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onDeleteSharedContent on type Subscription"}]}
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?
type SharedContent
@model
@auth(rules: [
{ allow: owner, identityClaim: "custom:user_id" },
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
content: String
owner: String
}
Requires you to pass an idToken instead of an accessToken in Amplify configure:
Amplify.configure({
...awsConfig,
graphql_headers: async () => {
try {
const session = await Auth.currentSession();
const token = session.idToken.jwtToken;
return { Authorization: token };
} catch {}
},
});
Generates the following subscriptions:
onCreateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema works as expected.
type SharedContent
@model
@auth(rules: [
{ allow: owner, identityClaim: "custom:user_id", operations: [create, read, update] },
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
content: String
owner: String
}
Requires you to pass an idToken instead of an accessToken in Amplify configure:
Amplify.configure({
...awsConfig,
graphql_headers: async () => {
try {
const session = await Auth.currentSession();
const token = session.idToken.jwtToken;
return { Authorization: token };
} catch {}
},
});
Generates the following subscriptions:
onCreateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema doesn't work as expected. The onDeleteSharedContent subscription fails, and result in the following warnings/errors:
[WARN] 37:22.453 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onDeleteSharedContent on type Subscription"}]}
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?
type SharedContent
@model
@auth(rules: [
{ allow: owner },
{ allow: private, provider: iam },
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["createSharedContent"]) @aws_iam @aws_cognito_user_pools
onUpdateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["updateSharedContent"]) @aws_iam @aws_cognito_user_pools
onDeleteSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"]) @aws_iam @aws_cognito_user_pools
This schema doesn't work as expected. Subscriptions work, but users see other user's content. Queries properly return only the owner's content.
Similar to example from docs: https://docs.amplify.aws/cli/graphql-transformer/directives#multiple-authorization-rules
type SharedContent
@model
@auth(rules: [
{ allow: owner },
{ allow: owner, ownerField: "editors", operations: [update, read] }
])
{
id: ID!
content: String
owner: String
editors: [String]
}
Generates the following subscriptions:
onCreateSharedContent(owner: String!, editors: String!): SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent(owner: String!, editors: String!): SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent(owner: String!, editors: String!): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema doesn't work as expected. Subscriptions fail, and result in the following warnings/errors:
[WARN] 17:46.494 DataStore - Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onUpdateSharedContent'"}]}
[WARN] 17:46.579 DataStore - Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument editors @ 'onDeleteSharedContent'"}]}
[WARN] 17:46.627 DataStore - Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument editors @ 'onCreateSharedContent'"}]}
type SharedContent
@model
@auth(rules: [
{ allow: owner, identityClaim: "custom:user_id" },
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent(owner: String): SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema doesn't work as expected. Subscriptions fail, and result in the following warnings/errors:
[WARN] 15:09.631 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onCreateSharedContent on type Subscription"}]}
[WARN] 15:09.700 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onUpdateSharedContent on type Subscription"}]}
[WARN] 15:09.741 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onDeleteSharedContent on type Subscription"}]}
Based on recent fix: https://github.com/aws-amplify/amplify-cli/pull/4340
type SharedContent
@model
@auth(rules: [
{ allow: groups, groups: ["Member"], operations: [read] },
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent: SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent: SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent: SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
This schema works as expected, but the recent fix only seems relevant if you use group auth.
type SharedContent
@model(subscriptions: { level: public })
@auth(rules: [
{ allow: owner, operations: [create, read] },
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
content: String
owner: String
}
Generates the following subscriptions:
onCreateSharedContent: SharedContent @aws_subscribe(mutations: ["createSharedContent"])
onUpdateSharedContent: SharedContent @aws_subscribe(mutations: ["updateSharedContent"])
onDeleteSharedContent: SharedContent @aws_subscribe(mutations: ["deleteSharedContent"])
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!
@manueliglesias Thank you! I appreciate the update.
Hi there @manueliglesias any good news to share so far?
Any ETA on when DataStore will work correctly with @auth directives as this currently doesn't work and security is an integral part of any application
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 Message
type has owner - create
authz, then how do I have other users get subscriptions to when new messages are posted?
Hi there guys, any news on this?
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.
GraphQL Validation Error
Schema#11 has 2 owners, owner: String
and editors: [String]
@auth(rules: [
{ allow: owner },
{ allow: owner, ownerField: "editors", operations: [update, read] }
])
When the DataStore
subscribes to events, the messages thrown are GraphQL validation errors:
[WARN] 17:46.627 DataStore - Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument editors @ 'onCreateSharedContent'"}]}
The reason for this is the way the DataStore
builds the GraphQL subscription. Looking at the DataStore code, it will iterate all items with the owner
AuthStrategy
, and select the last one.
ownerAuthRules.forEach(ownerAuthRule => {
const ownerValue = cognitoTokenPayload[ownerAuthRule.identityClaim];
if (ownerValue) {
result = {
authMode: GRAPHQL_AUTH_MODE.AMAZON_COGNITO_USER_POOLS,
isOwner: ownerAuthRule.areSubscriptionsPublic ? false : true,
ownerField: ownerAuthRule.ownerField,
ownerValue,
};
}
});
The ownerAuthRules
list is obtained from the generated schema src/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.
Authentication Error
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._
@auth(rules: [
{ allow: owner, operations: [create, read] },
{ allow: groups, groups: ["Admin"] }
])
[WARN] 33:56.520 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onDeleteSharedContent on type Subscription"}]}
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 group
AuthStrategy
has 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.
## [Start] Static Group Authorization Checks **
#set($isStaticGroupAuthorized = $util.defaultIfNull(
$isStaticGroupAuthorized, false))
## Authorization rule: { allow: groups, groups: ["Admin"], groupClaim: "cognito:groups" } **
#set( $userGroups = $util.defaultIfNull($ctx.identity.claims.get("cognito:groups"), []) )
#set( $allowedGroups = ["Admin"] )
#foreach( $userGroup in $userGroups )
#if( $allowedGroups.contains($userGroup) )
#set( $isStaticGroupAuthorized = true )
#break
#end
#end
## [End] Static Group Authorization Checks **
It will then check the owner passed in the subscription against the username in the JWT.
## [Start] Owner Authorization Checks **
#set( $isOwnerAuthorized = false )
## Authorization rule: { allow: owner, ownerField: "owner", identityClaim: "cognito:username" } **
#set( $allowedOwners0 = $util.defaultIfNull($ctx.args.owner, null) )
#set( $identityValue = $util.defaultIfNull($ctx.identity.claims.get("username"),
$util.defaultIfNull($ctx.identity.claims.get("cognito:username"), "___xamznone____")) )
#if( $util.isList($allowedOwners0) )
#foreach( $allowedOwner in $allowedOwners0 )
#if( $allowedOwner == $identityValue )
#set( $isOwnerAuthorized = true )
#end
#end
#end
#if( $util.isString($allowedOwners0) )
#if( $allowedOwners0 == $identityValue )
#set( $isOwnerAuthorized = true )
#end
#end
## [End] Owner Authorization Checks **
And finally fails if either of the checks above is successful.
## [Start] Throw if unauthorized **
#if( !($isStaticGroupAuthorized == true || $isOwnerAuthorized == true) )
$util.unauthorized()
#end
## [End] Throw if unauthorized **
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.
Hi there @abdielou thanks for your feedback, following your suggestion I changed the declaration order for the auth rules
type Task
@model
@auth(
rules: [
{ allow: owner, ownerField: "tec", operations: [update, read] }
{ allow: owner }
]
) {...}
and sadly the same issue came up
{"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument tec @ 'onUpdateTask'"}]}
So no, changing the order doesn't solve the subscription issue
@alexandprivate
Change the order in the generated schema src/models/schema.js
Just in case, make sure you re-generate the models, then check the schema.
@alexandprivate Actually I did not look well at your error. Your error is now complaining about tec
and not owner
. That means that the order was indeed changed, but the subscription is requiring tec
. What I would suggest you is to add a new Subscription at your schema amplify/backend/api/<yourapiname>/schema.graphql
.
Something like :
type Subscription {
onTaskUpdates(tec: ID): Task
@aws_subscribe(mutations: ["updateTask"])
}
I seem to be hitting this issue using multiple auths even when _not_ using an owner-based auth;
type MyModel
@auth (rules: [
{allow: public, provider: apiKey}
{allow: groups, groups: ["Managers"]}
])
Subscriptions return null for everything other than any ID
field.
The apiKey provider is used to update a model from a Lambda function.
Hi there @abdielou I am using "npm run amplify-modelgen" to generate my models and using DataStore, therefor I'm not writing my own subscriptions instead I'm just defined the models for each table. That's the whole situation here since Amplify is unable to add the right keys to the subscription by running the above script when more than one auth rule is defined.
Lately, I've found another issue, by using predicates, auth rules seem to be totally ignored if you are setting a model only accessible by the owner, and the query es call using predicates the auth owner rules is ignored and every item from the DB that fulfills the predicate logic is returned even those owned by another user.
I am using "npm run amplify-modelgen" to generate my models and using DataStore
What I do is that I edit the generated Schema at AppSync and the generated code locally. Although there might be a better way to do this, I really don't know yet.
Lately, I've found another issue, by using predicates, auth rules seem to be totally ignored if you are setting a model only accessible by the owner, and the query es call using predicates the auth owner rules is ignored and every item from the DB that fulfills the predicate logic is returned even those owned by another user.
That's weird. We need a maintainer here.
@elorzafe @manueliglesias @ericclemmons @iartemiev @ashika01 🙏
@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
Thanks @abdielou I'll take a look at it!
Hi there @abdielou looking at the generated schema in backend/api/APINAME/build/schema.graphql the generated subscription looks like
type Subscription {
...
onUpdateTask(tec: String!, owner: String!): Task @aws_subscribe(mutations: ["updateTask"])
...
}
Schema used to generate the build
type Task
@model
@auth(
rules: [
{ allow: owner, ownerField: "tec", operations: [read, update] }
{ allow: owner, ownerField: "owner" }
]
) {
...
tec: String
owner: String
}
Generated schema.js rules at src/models for model "Task"
"rules": [
{
"provider": "userPools",
"ownerField": "tec",
"allow": "owner",
"operations": [
"read",
"update"
],
"identityClaim": "cognito:username"
},
{
"provider": "userPools",
"ownerField": "owner",
"allow": "owner",
"identityClaim": "cognito:username",
"operations": [
"create",
"update",
"delete"
]
}
]
Yet the same warning is showed on the console
[WARN] 30:48.220 DataStore - Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument tec @ 'onUpdateTask'"}]}
Can confirm https://github.com/aws-amplify/amplify-js/issues/6108#issuecomment-647708908
Does NOT work:
@auth(rules: [
{ allow: private, operations: [read] },
{ allow: owner }
])
Works
@auth(rules: [
{ allow: groups, groups: ["Admin"] }
{ allow: owner }
])
[WARN] 30:48.220 DataStore - Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument tec @ 'onUpdateTask'"}]}
@alexandprivate From what I understand, the DataStore only passes ONE owner (the last one in the list) if @auth is enabled.
Also, the DataStore generates it's own subscription queries (GQL), thus it will never use custom subscriptions. There's no point in defining custom subscriptions if you are only using the DataStore.
The only workaround I can give you is to manually edit your generated Schema at the AppSync console. You can remove the requirement for argument tec
and it will still work. But just be careful with any side effects on your business rules.
The example I shared was for GQL only, not DataStore (had to start somewhere). I'm looking into a solution for re-enabling the DataStore now, but it involves modifying its architecture. Hopefully, the maintainers are already working on a solution or can provide me with some guidance to build some advanced options.
For those looking to use DataStore for Offline Support with cloud sync... I'm working/exploring a hackaround https://github.com/aws-amplify/amplify-cli/issues/4794#issuecomment-670726821
Schema auth rule
@auth(rules: [
{ allow: owner },
{ allow: owner, ownerField: "editors", operations: [update, read] }
])
After codegen models and push, app reports the following error in the generated resolver:
DataStore - subscriptionError Connection failed:
{"errors":[
{"message":"Validation error of type MissingFieldArgument: Missing field argument editors @ 'onCreate#####'"}
]}
I don't know how helpful (or not) this is to this thread, but while troubleshooting a similar (but ultimately different) problem, I found that DataStore swallows errors that might be produced while pulling creds, which means undefined vars get passed down as creds uncaught.
I think this creates a scenario where the logic that would otherwise throw an error message and short circuit the attempt to create the subscription gets skipped over - which may be masking a higher level problem where a cred is not setup correctly.
https://github.com/aws-amplify/amplify-js/blob/main/packages/datastore/src/sync/processors/subscription.ts#L240-L269
https://github.com/aws-amplify/amplify-js/blob/main/packages/datastore/src/sync/processors/subscription.ts#L61-L68
https://github.com/aws-amplify/amplify-js/blob/main/packages/datastore/src/sync/processors/subscription.ts#L302-L312
If your @auth
directive has more than 1 owner, the DataStore will only pick up the last one.
That's what I said here: https://github.com/aws-amplify/amplify-js/issues/6108#issuecomment-667991419
I hypothesize that a workaround to this is custom resolvers, where you do the check on either members
or owner
. That's what the generated resolvers do when you specify multiple auth rules (eg: owner + members). The problem is the DataStore because, as I said, it has an expectation of only 1 owner
.
A possible workaround is the following:
amplify/backend/api/membersexample/build/resolvers
) into custom resolvers (amplify/backend/api/membersexample/resolvers
).NOTE: This is a hypothesis. Currently testing. Will come back with results.
@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 SyncEngine
so 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.
I get this error as well, I believe because I forbid the delete operation on my models.
My schema:
type Plant
@model
@auth(rules: [{ allow: owner, operations: [create, read, update] }]) {
id: ID!
name: String!
timeOfDeath: AWSDateTime
lastWateredAt: AWSDateTime
lastFertilizedAt: AWSDateTime
wateringPeriodInDays: Int!
fertilizingPeriodInDays: Int
events: [PlantEvent!]! @connection(keyName: "byPlant", fields: ["id"])
waterNextAt: AWSDate!
fertilizeNextAt: AWSDate
}
enum PlantEventType {
WATERED
FERTILIZED
}
type PlantEvent
@model
@key(name: "byPlant", fields: ["plantId", "createdAt"])
@auth(rules: [{ allow: owner, operations: [create, read, update] }]) {
id: ID!
plantId: ID!
plant: Plant! @connection(fields: ["plantId"])
type: PlantEventType!
createdAt: AWSDateTime!
}
DataStore emits the following error:
[WARN] 48:14.452 DataStore - subscriptionError Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onDeletePlant'"}]}
which I assume is because there is no onDeletePlant
since Plant
is not permitted to be deleted.
@chuckatc We have made some improvements recently in aws-amplify
as well as the CLI particularly around this issue. Could you try upgrading to the latest versions of each and see if you're still seeing the same behavior?
Resolving as these use cases are fixed and within the latest version of aws-amplify
. Please let us know if you are still experiencing these issues. Thanks!
@sammartinez I uploaded the latest version of aws-amplify
and I am still experiencing the same issue when adding an editors
field to my model:
DataStore - subscriptionError Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument editors @ 'onCreateDraft'"}]}
I am following the amplify docs with a model similar to this one:
type Draft @model
@auth(rules: [
{ allow: owner, ownerField: "editors", operations: [update, read] }
]) {
id: ID!
title: String!
content: String
owner: String
editors: [String]
}
I am not sure if this is related but I have also experienced an issue in the past when there is a field of type array without any @connection
. I mentioned this error on this post 6965
@hmisonne That use case with an array of "owners" is a slightly different than the original issue here. Could you open a new issue so we can more precisely look into the problems you're having?
@amhinson Done, I described my issue here #7069 !
The DataStore
is still having problems when multiple auth rules are defined. Here is an example with only 1 owner defined. I have tried several different combinations too but the result is the same: items are not returned to the client. The desired outcome is that owners are allowed to CRUD and everyone else is allowed to read the object.
It is worth noting that the warning message in console changes to a validation error of type MissingFieldArgument when the owner field is not specified in the schema; however, documentation says this field does not need to be included. I don't know if that is related to the issue or not.
type Post
@model
@auth(rules: [{ allow: owner }, { allow: private, operations: [read] }]) {
id: ID!
title: String!
owner: String # Tried with and without this field
}
@gmreburn You shouldn't need the private
auth rule for the use case you're describing. Try removing it and specifying the following operations for owner:
@auth(rules: [{ allow: owner, operations: [create,update,delete] }])
thanks for the reply @iartemiev, I thought that worked but another issue quickly followed :)
Objects are not being created in the remote DS and AWSAppSyncRealTimeProvider
throws warnings for onCreate, onUpdate and onDelete after adding and pushing the operations: [create,update,delete]
changes to all models in the schema. I don't have any subscriptions enabled at the moment. I think it is caused by a one to many or many to many model relationship but I'm not certain yet. In any case, new objects are not being sent to the remote DS. I will try to identify the root cause and open a separate issue, if warranted.
@gmreburn are those warnings referencing the same model, i.e., Post
or a different one?
Did you run amplify push
followed by amplify codegen models
?
I don't have any subscriptions enabled at the moment.
Whether you're explicitly calling DataStore.observe
in your code or not, DataStore always uses subscriptions to keep your local store in sync with your cloud storage.
I blacked it out because it was specific to my project but similar to Post (from what I recall). I did amplify push -y
followed by amplify codegen models
after 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.
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.
Most helpful comment
@brene @Ashish-Nanda @undefobj @SwaySway @amhinson @manueliglesias @sammartinez @yuth @iartemiev @elorzafe @UnleashedMind
DataStore
@auth
directive 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
@auth
directive.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
@auth
rules 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
@auth
directive 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.
{ allow: owner }
{ allow: owner, operations: [create, update, delete] }
{ allow: owner, operations: [create, update, delete] }
{ allow: private, operations: [read] }
{ allow: owner }
{ allow: private, provider: iam }
{ allow: owner }
{ allow: groups, groups: ["Admin"] }
{ allow: owner, operations: [create, read] }
{ allow: groups, groups: ["Admin"] }
{ allow: owner, operations: [create, read, update] }
{ allow: groups, groups: ["Admin"] }
{ allow: owner, identityClaim: "custom:user_id" }
{ allow: groups, groups: ["Admin"] }
{ allow: owner, identityClaim: "custom:user_id", operations: [create, read, update] }
{ allow: groups, groups: ["Admin"] }
{ allow: owner }
{ allow: private, provider: iam }
{ allow: groups, groups: ["Admin"] }
{ allow: owner }
{ allow: owner, ownerField: "editors", operations: [update, read] }
{ allow: owner, identityClaim: "custom:user_id" }
{ allow: groups, groups: ["Admin"] }
{ allow: groups, groups: ["Member"], operations: [read] }
{ allow: groups, groups: ["Admin"] }
subscriptions: { level: public }
{ allow: owner, operations: [create, read] }
{ allow: groups, groups: ["Admin"] }
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.