Amplify-js: Datastore not working properly with owner @auth rule on React Native

Created on 26 May 2020  路  19Comments  路  Source: aws-amplify/amplify-js

Describe the bug
I am not able to use Datastore in my Expo app because of a problem with the owner @auth rule in my schema. When my app is starting, Datastore is initializing and I get the following warning in the console:

[INFO] 31:40.933 Reachability - subscribing to reachability in React Native
[INFO] 31:40.970 Reachability - Notifying initial reachability state null
[INFO] 31:42.959 Reachability - Notifying reachability change true

[WARN] 31:43.842 DataStore - Sync error, subscription failed Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onDeleteCall'"}]}
- node_modules/expo/build/environment/muteWarnings.fx.js:18:23 in warn
* [native code]:null in warn
* http://192.168.1.16:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&minify=false&hot=false:125761:12 in <unknown>
* http://192.168.1.16:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&minify=false&hot=false:125794:22 in <unknown>
- node_modules/@aws-amplify/datastore/lib-esm/datastore/datastore.js:659:35 in sync.start.subscribe$argument_0.error
- node_modules/zen-observable/lib/Observable.js:139:8 in notifySubscription
* http://192.168.1.16:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&minify=false&hot=false:129987:23 in onNotify
- node_modules/zen-observable/lib/Observable.js:239:11 in error
- node_modules/@aws-amplify/datastore/lib-esm/sync/index.js:141:56 in __generator$argument_1
* http://192.168.1.16:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&minify=false&hot=false:127369:27 in step
- node_modules/tslib/tslib.js:122:34 in <anonymous>
- node_modules/tslib/tslib.js:113:43 in rejected
- node_modules/promise/setimmediate/core.js:37:14 in tryCallOne
- node_modules/promise/setimmediate/core.js:123:25 in setImmediate$argument_0
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:146:14 in _callTimer
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:194:17 in _callImmediatesPass
- node_modules/react-native/Libraries/Core/Timers/JSTimers.js:458:30 in callImmediates
* [native code]:null in callImmediates
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:407:6 in __callImmediates
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:143:6 in __guard$argument_0
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:384:10 in __guard
- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:142:17 in __guard$argument_0
* [native code]:null in flushedQueue
* [native code]:null in callFunctionReturnFlushedQueue

It looks like Datastore does not pass required owner attribute when it subscribes to changes.

To Reproduce

  1. In an Expo app, add an owner rule in your schema: @auth(rules: [{allow: owner}]).
  2. amplify push
  3. npm run amplify-modelgen or amplify codegen models
  4. npm start
  5. When using Datastore, you will see the warning in the console.

Expected behavior
At least no warning in the console.

What is Configured?
cli:
@aws-amplify/[email protected]
package.json:

"@aws-amplify/api": "^3.1.10",
"@aws-amplify/auth": "^3.2.7",
"@aws-amplify/core": "^3.2.7",
"@aws-amplify/datastore": "^2.1.0",

Thanks for your help.

DataStore React Native to-be-reproduced

All 19 comments

Hi @MrHertal

Can you try the suggestion in this comment:

https://github.com/aws-amplify/amplify-js/issues/5687#issuecomment-631185661

Hi @manueliglesias

I have tried and it still does not work.

It seems to me that it still the early age of Datastore, specially with React Native (it was released a couple of months ago for React Native). I have tried to set up the Datastore in my app, and my conclusion is that as long as you have a simple schema, without @auth rules, it's working well.

I have many types and @auth rules in my app schema. For now I managed to make the Datastore work with only one type.

For instance :

type Call
@model
@auth(rules: [
  {allow: owner}
  {allow: private, provider: iam}
])
{
  id: ID!
  owner: String
  duration: Int
}

This type does not work with Datastore and produce error above.

type Company
@model(subscriptions: {level: public})
@auth(rules: [
  {allow: public, operations: [read]},
  {allow: private, operations: [read]}
  {allow: private, provider: iam}
])
{
  id: ID!
  name: String!
}

This type works with the Datastore but I had to use a workaround to make it work. After generating models with amplify codegen models, I removed those lines in src/models/schema.js:

                  {
                    "type": "auth",
                    "properties": {
                        "rules": [
                            {
                                "allow": "public",
                                "operations": [
                                    "read"
                                ]
                            },
                            {
                                "allow": "private",
                                "operations": [
                                    "read"
                                ]
                            },
                            {
                                "allow": "private",
                                "provider": "iam"
                            }
                        ]
                    }
                }

It is weird that I have to do this to make it work.

@MrHertal When you added in the owner did you run amplify codegen models again? Also are you logging in with the Auth category in your app? The issue is that @auth with owner based authorization requires an owner field to be passed in the subscription argument, which DataStore uses from the Auth category. However modelgen doesn't automatically add this to your GraphQL schema so you must do that manually. We're tracking an enhancement to change this in the DX.

@MrHertal When you added in the owner did you run amplify codegen models again? Also are you logging in with the Auth category in your app? The issue is that @auth with owner based authorization requires an owner field to be passed in the subscription argument, which DataStore uses from the Auth category. However modelgen doesn't automatically add this to your GraphQL schema so you must do that manually. We're tracking an enhancement to change this in the DX.

Yes I ran amplify codegen models again after adding the owner field, and yes I am logged in with the Auth category.

I let the cli generates the queries, mutations and subscriptions so in src/graphql/subscriptions.js I have this:

export const onCreateCall = /* GraphQL */ `
  subscription OnCreateCall($owner: String!) {
    onCreateCall(owner: $owner) {
      id
      owner
      duration
      _version
      _deleted
      _lastChangedAt
      createdAt
      updatedAt
    }
  }
`;

There is also OnUpdateCall and OnDeleteCall.

So yes the issue is simple, Datastore does not pass the owner variable when it subscribes.

Note that using the Amplify client, I am able to subscribe to Call updates like this:

import API from '@aws-amplify/api';
import {onUpdateCall} from '../../../graphql/subscriptions';

// const owner = Cognito ID of the logged in user

const subscription = API.graphql({
  query: onUpdateCall,
  variables: {owner},
  authMode: 'AMAZON_COGNITO_USER_POOLS',
}).subscribe({
  error: (e) => console.log(e),
  next: (evt) => console.log(evt),
});

In order to subscribe, I am passing manually the id of the logged in user as the owner variable, and it works well. So if Datastore did the same it would work :)

Thanks @MrHertal. This is supported in DataStore as @manueliglesias link and there are many customers who've used successfully, I think there's something up with the configuration or perhaps you've found a codepath that needs more investigation. Could you provide a link to a gist/repo of your code (or a similar reproduction of the issue) so that we can review this upcoming week?

Thanks @MrHertal. This is supported in DataStore as @manueliglesias link and there are many customers who've used successfully, I think there's something up with the configuration or perhaps you've found a codepath that needs more investigation. Could you provide a link to a gist/repo of your code (or a similar reproduction of the issue) so that we can review this upcoming week?

I started a new project and was able to reproduce the issue. Here is the repo:

https://github.com/MrHertal/datastore-test

Everything worked fine until this commit:

https://github.com/MrHertal/datastore-test/commit/0bc9a241902ec0b97e0ef1e609297f616f0945b5

You will find in the README the config I used with the CLI to configure Amplify.

Thanks for your help.

Thanks to all for looking into these issues. What is the status of DataStore functionality with other types of @auth, something a bit more involved such as:

{ allow: groups, groups: ["Admins", "Editors"] } ?

Or:

{ allow: groups, groupsField: "myGroups", operations: [create, update, read] } ?

Planning to build an application that will need @auth directives like the ones above and are wondering if it would be possible to make it work with DataStore.

Thanks again!
Santiago

I think problem is in

  {allow: private, provider: iam}

As workaround you can remove iam auth and run amplify codegen models

It helped me.

I think problem is in

  {allow: private, provider: iam}

As workaround you can remove iam auth and run amplify codegen models

It helped me.

Hi, I tried this but I still have the warning.

@MrHertal did you push changes?

You mean, did I amplify push and amplify codegen models, yes don't worry :)

I did not push in the repository because I still have the warning, and I need that @auth rule anyway because I have a Lambda function that manipulates calls.

@MrHertal: I can confirm this behavior

1) Prepare model and push to aws backend

@auth(rules: [{ allow: owner }, { allow: private, provider: iam}])

npm run amplify-modelgen

amplify push

Not working on client side:
ConsoleLogger.ts:99 [WARN] 44:15.805 DataStore - Sync error subscription failed Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onCreateLala'"}]}

2) Only change model on client side as workaround (do not push model to aws backend)

@auth(rules: [{ allow: owner }

npm run amplify-modelgen

works

@MrHertal: I can confirm this behavior

1) Prepare model and push to aws backend

@auth(rules: [{ allow: owner }, { allow: private, provider: iam}])

npm run amplify-modelgen

amplify push

Not working on client side:
ConsoleLogger.ts:99 [WARN] 44:15.805 DataStore - Sync error subscription failed Connection failed: {"errors":[{"message":"Validation error of type MissingFieldArgument: Missing field argument owner @ 'onCreateLala'"}]}

2) Only change model on client side as workaround (do not push model to aws backend)

@auth(rules: [{ allow: owner }

npm run amplify-modelgen

works

Thanks @lenarmazitov and @small4all, I have tested that and I don't have the warning anymore! But the calls don't sync...

Here is the commit:

https://github.com/MrHertal/datastore-test/commit/c8d798b3dd578ece13f9bd346678af8e6605ac26#diff-9c67ad799cff6b9698d6f01b26c960b0L50

I get this in the console:

[INFO] 59:51.831 Reachability - subscribing to reachability in React Native
[INFO] 59:51.882 Reachability - Notifying initial reachability state null
[INFO] 59:53.850 Reachability - Notifying reachability change true

calls Array []

Of course there are calls in my database with the owner field correctly set.

I'm also wrestling with this very same issue currently and trying to go the route of manually setting the owner field, but I'm not sure what that should be when I add a new record. Is the owner field a property on the current auth? @undefobj

@imoby you don't need to populate the owner field in a DataStore.save() as it's automatically set in the service resolver code, you just need to add it to your schema as outlined above.

@undefobj hmmm that鈥檚 interesting because I鈥檝e done that in my react native iOS app and I still can鈥檛 get the subscription to work at all across two devices with the same user signed in and the models have auth to only allow owner. Is this still a issue that needs to be fixed or maybe am I missing something?

@imoby I'd recommend opening a new issue with your full code samples for someone to take a look at.

Some update about this issue.
I still cannot use the DataStore with the @auth rules of my project.

I have the following schema:

type Company
  @model(subscriptions: { level: public })
  @auth(
    rules: [
      { allow: public, operations: [read] }
      { allow: private, operations: [read] }
      { allow: private, provider: iam }
    ]
  ) {
  id: ID!
  name: String!
}

type Call
  @model
  @auth(rules: [{ allow: owner }, { allow: private, provider: iam }]) {
  id: ID!
  owner: String
  duration: Int
}

After running amplify codegen models I manually remove the different auth rules in src/models/schema.js and I only keep the owner rule (see https://github.com/MrHertal/datastore-test/blob/master/src/models/schema.js).

Company works, I can list and get them.
Call does not work.

DataStore executes sync queries but it fails:

{
  "data": { "syncCalls": null },
  "errors": [
    {
      "path": ["syncCalls"],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [{ "line": 2, "column": 3, "sourceName": null }],
      "message": "Not Authorized to access syncCalls on type Query"
    }
  ]
}

I realized that maybe the DataStore is using the wrong auth mode.
So in aws-exports.js, I changed:

"aws_appsync_authenticationType": "API_KEY",

to

"aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",

Now it works better and all the calls are synced !

But I get a new warning regarding companies:

ConsoleLogger.js:97 [WARN] 13:44.938 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onCreateCompany on type Subscription"}]}

ConsoleLogger.js:97 [WARN] 13:44.993 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onUpdateCompany on type Subscription"}]}

ConsoleLogger.js:97 [WARN] 13:44.995 DataStore - Connection failed: {"errors":[{"errorType":"Unauthorized","message":"Not Authorized to access onDeleteCompany on type Subscription"}]}

So I can list all companies on page load, but I cannot have live updates of companies.

What I learnt is that DataStore is using project default auth mode to sync. So when using different auth modes inside a project, it will not sync well. We should be able to pass an option to DataStore in order to choose the auth mode depending on the model (here Company or Call). The same way we can choose the auth mode when executing a query with the Amplify client (https://docs.amplify.aws/lib/graphqlapi/authz/q/platform/js).

I removed the subscription rule for Company:

type Company
  @model
  @auth(
    rules: [
      { allow: public, operations: [read] }
      { allow: private, operations: [read] }
      { allow: private, provider: iam }
    ]
  ) {
  id: ID!
  name: String!
}

Now the DataStore is working for both Company and Call !

I'm closing this issue.

Was this page helpful?
0 / 5 - 0 ratings