aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.44k stars 2.13k forks source link

DataStore with @auth - Sync error subscription failed ... Missing field argument owner #6108

Closed chuckatc closed 4 years ago

chuckatc commented 4 years ago

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

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)

kaustavghosh06 commented 4 years ago

@chuckatc We're actively working on this and expect a fix for this very soon. cc @yuth @elorzafe

dconlon-wagetap commented 4 years ago

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.

dbhagen commented 4 years ago

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"
};
CodySwannGT commented 4 years ago

Not much to add - same as the others. Fix or a workaround would be fantastic as we're in production with no path forward.

elorzafe commented 4 years ago

@dbhagen @chuckatc @dconlon-monigo @CodySwannGT the fix for the library is already published on @latest tag on npm.

ashteya commented 4 years ago

@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?

mkaschke commented 4 years ago

same here!

elorzafe commented 4 years ago

@ashteya @mkaschke can you share your package.json and schema.graphql files?

mkaschke commented 4 years ago

@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
}
elorzafe commented 4 years ago

@mkaschke can you rename the type Subscription from annotated schema. That is overwriting the Subscription generated by CLI

elorzafe commented 4 years ago

@mkaschke you should also add @aws-amplify/datastore to your package.json file.

chuckatc commented 4 years ago

@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).

yuth commented 4 years ago

@chuckatc @mkaschke Please feel free to re-open/comment in this thread if you still facing issues

alex-vladut commented 4 years ago

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",
elorzafe commented 4 years ago

@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?

alex-vladut commented 4 years ago

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

chuckatc commented 4 years ago

@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.

yuth commented 4 years ago

Please feel free to comment in the thread if auth does not work with DataStore

richardfan1126 commented 4 years ago

@alex-vladut is correct. You save my life!!

tomheno commented 4 years ago

Hi ! I've been struggling with this issue for a while

Using

"@aws-amplify/datastore": "^2.0.8" CLI : 4.18.0

Schema

type Session
  @auth(
    rules: [
      {
        allow: owner
      }
    ]
  )

  @model {
  id: ID!
  owner: String
  title: String
}

Error given

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)

richardfan1126 commented 4 years ago

@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?

tomheno commented 4 years ago

@richardfan1126 Got it from the console, Amplify Logger is logging at DEBUG level, see the following screenshot : image There's also the same error with onUpdateSession & onDeleteSession subscriptions, that are run by DataStore in background, those messages occures on app start.

veproza commented 4 years ago

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",
johanbuys commented 4 years ago

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",
MrHertal commented 4 years ago

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?

lenarmazitov commented 4 years ago

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.

yashprit commented 4 years ago

We have similar issues, all models are marked with @auth

  1. Cognito is setup separately and configured in aws-exports
  2. AppSync api pushed and configured in 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.

MrHertal commented 4 years ago

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 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.

https://github.com/aws-amplify/amplify-js/issues/5898

rpostulart commented 4 years ago

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",

alexandprivate commented 4 years ago

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",

lenarmazitov commented 4 years ago

Hello @alexandprivate is there missing comma in auth array? Please check with comma, if it works let us know.

alexandprivate commented 4 years ago

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

alexandprivate commented 4 years ago
Screen Shot 2020-06-14 at 19 48 52

Ok guys lets see if we can solve this

alexandprivate commented 4 years ago

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?

johanbuys commented 4 years ago

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.

alexandprivate commented 4 years ago

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?

gavinsawyer commented 4 years ago

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.

johanbuys commented 4 years ago

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.

rpostulart commented 4 years ago

@yuth @elorzafe @undefobj Can you re-open this issue? It is still not solved in the last relase!

undefobj commented 4 years ago

Reopening cc @sammartinez @manueliglesias @iartemiev

sammartinez commented 4 years ago

Thanks @undefobj, Im going to transfer this to the Amplify JS Repo so we can dig into it

alexandprivate commented 4 years ago

Thank you so much guys for looking into this again, let us know, so we can update our schemas

mdoesburg commented 4 years ago

@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:

mdoesburg commented 4 years ago

@brene @Ashish-Nanda @undefobj @SwaySway @amhinson @manueliglesias @sammartinez @yuth @iartemiev @elorzafe @UnleashedMind

DataStore @auth directive combinations

Like 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:

  1. Log users out of iOS and Android simulators
  2. Modify schema
  3. Run amplify push
  4. Run amplify codegen models
  5. Reload devices
  6. Log in with 2 different users
  7. Observe model on DataStore
  8. Try creating/updating new model from both devices

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) #

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.

Schema 2 #

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'"}]}

Schema 3 #

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'"}]}

Schema 4 #

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'"}]}

Schema 5 (works) #

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.

Schema 6 #

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?

Schema 7 #

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?

Schema 8 (works) #

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.

Schema 9 #

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?

Schema 10 #

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.

Schema 11 #

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'"}]}

Schema 12 #

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"}]}

Schema 13 (works) #

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.

Schema 14 (works) #

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.

manueliglesias commented 4 years ago

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!

mdoesburg commented 4 years ago

@manueliglesias Thank you! I appreciate the update.

alexandprivate commented 4 years ago

Hi there @manueliglesias any good news to share so far?

frankclaassen commented 4 years ago

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

a5an0 commented 4 years ago

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?

alexandprivate commented 4 years ago

Hi there guys, any news on this?