Amplify-js: Field Level @auth declarations throw errors?

Created on 2 Aug 2019  Â·  21Comments  Â·  Source: aws-amplify/amplify-js

* Which Category is your question related to? *
API
* What AWS Services are you utilizing? *
AppSync
* Provide additional details e.g. code snippets *

type User @model { name: String balance: Int @auth(rules: [ {allow: groups, groups: ["Admins"], operations: [update, create, read]}, {allow: owner, ownerField: "id", operations: [read]}, ]) }

It appears I get an error if I issue a graphql query for name and balance.

Meaning, I have to issue two different queries depending on whether the logged in user is an admin or not.

Seems to me that if I try to read a particular field and don't have permissions, it should just return null.

AppSync GraphQL pending-close-response-required

All 21 comments

This is even further complicated because we want the model owner to be able to read the "balance" property but not other users.

So, take the example of User X visiting User Y's profile vs User Y visiting User Y's profile. We have to issue two different queries to get User Y's profile - one without the "balance" field and one WITH the balance field.

@CodySwannGT can you add code snippet of your queries?.

This seems an issue more related to amplify cli, I will transfer it to that repo.

Thanks!

type User @model { name: String balance: Int @auth(rules: [ {allow: groups, groups: ["Admins"], operations: [update, create, read]}, {allow: owner, ownerField: "id", operations: [read]}, ]) }

Would generate this in graphql/queries

export const getUser = `query GetUser($id: ID!) { getUser(id: $id) { id name balance createdAt updatedAt } }

But then if I use the auto-generated query, I will get an error because I'm including the "balance" field in my query

````
import { getUser } from '../graphql/queries';
import { useQuery } from 'react-apollo-hooks';

....
//// this will throw an error because I'm not signed in as an "Admins". Desired behavior is to return null for balance
const { data: {getUser: {} = {}} = {} } = useQuery(gql(getUser), {
variables: {
id: userId
}
});
....
````

The workaround, of course, is to write my own 'getUser' query that omits the balance field

````
import { getUser } from '../graphql/queries';
import { getUserWithoutBalance } from '../custom/queries';
import { useQuery } from 'react-apollo-hooks';

....
//// this will throw an error because I'm not signed in as an "Admins". Desired behavior is to return null for balance
const { data: {getUser: {} = {}} = {} } = useQuery(gql(!!currentUser.isAdmin ? getUser : getUserWithoutBalance), {
variables: {
id: userId
}
});
....
````

But this seems wildly inefficient to maintain two separate queries

@CodySwannGT I agree this shouldn't be the ideal behavior. We're looking into this.

@CodySwannGT What type of error are you receiving on your query from the client? Is it a 4xx error?

Yes. 401 or 403 - I don't recall exactly

Yes a 403

On Tue, Aug 6, 2019 at 6:44 PM Kaustav Ghosh notifications@github.com
wrote:

@CodySwannGT https://github.com/CodySwannGT What type of error are you
receiving on your query from the client? Is it a 4xx error?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/aws-amplify/amplify-cli/issues/1972?email_source=notifications&email_token=AACHQO56XLYA6KTHKVXQFZ3QDH5EDA5CNFSM4IJMUTHKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD3WVPMY#issuecomment-518870963,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AACHQO4XNSBUO4XA5OHBZIDQDH5EDANCNFSM4IJMUTHA
.

--
Cody Swann - CEO, Gunner Technology
T: 213.915.4083 | [email protected] | www.gunnertech.com

http://www.gunnertech.com/
Check out our Live Show every Thursday at 1 pm EST on Facebook
https://www.facebook.com/gunnertechnology, YouTube
https://www.youtube.com/channel/UCjURDi2kurZOJFK1OY-QRHg and Periscope
https://www.periscope.tv/gunnertech.

CONFIDENTIALITY NOTICE: This email message, together with any documents,
files and/or email messages attached to it, is intended for the sole use of
the individual or entity to whom it is addressed, and may contain
information that is legally privileged, confidential and restricted from
disclosure. If you are not the intended recipient, or responsible for
delivery to that person, you are hereby notified that any dissemination or
copying of this communication is STRICTLY PROHIBITED. In such case please
notify the sender by reply email and delete this message without reading,
printing or saving.

@CodySwannGT I tried out experimenting with your schema

type User 
@model 
{
  name: String
  balance: Int @auth(rules: [
    {allow: groups, groups: ["Admins"], operations: [update, create, read]},
    {allow: owner, ownerField: "id", operations: [create, read]},
  ])
}

I had the following queries which i tested

query GetUser {
  getUser(id: "kaustav") {
    name
    balance
  }
}

mutation CreateUser {
  createUser(input: {name: "kaustav", balance: 123}) {
    name
    balance
  }
}

For non-admin case - for which you're saying you're getting an error, I got the following response from AppSync

{
  "data": {
    "getUser": {
      "name": "kaustav",
      "balance": null
    }
  },
  "errors": [
    {
      "path": [
        "getUser",
        "balance"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 33,
          "column": 5,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access balance on type Int"
    }
  ]
}

The AppSync response has an error block in it (due to the partial authorization the user has), but it also has the correct data block with the expected 'null' value on the balance field. Could you please verify your response data (in the browser network tab) and make sure you have both the data and the error field in the response?

Also, AppSync would return a 401 or 403 when there is an issue at the request level. Could you check in your network tab as to what's the error message?

So you're correct on the 4xx. There is none.

And here's the problem - when Apollo (which ships with AppSync) detects an error, no data is returned.

Here's a pseudo example for a NON "Admins" user

````
const { fetchMore, loading, error, data: {[queryName]: {items, nextToken} = {items: [], nextToken: null}} = {}} = useQuery(gql(query), {
variables
});

console.log("IM THE DATA from APOLLO QUERY", items)

console.log("IM THE ERROR", error)
````

https://www.dropbox.com/s/mdiz3s3lum8sylg/Screenshot%202019-08-06%2021.03.44.png?dl=0

Now with an "ADMINS" user

https://www.dropbox.com/s/1cttu02c9rmtnii/Screenshot%202019-08-06%2021.04.59.png?dl=0

I suppose you could say this is upstream with Apollo, but AppSync is documented hand-in-hand with Apollo, so I'm not sure who's lap this should fall in.

Edit - also since AppSync is a bit behind on version for Apollo, I'm locked at this:

"react-apollo": "2.5.6", "react-apollo-hooks": "0.4.5",

@CodySwannGT This looks like an issue with the AppSync JS SDK (and maybe the apollo sdk used underneath). I'm transferring this issue back to the Amplify JS team to look into this further.

Thanks.

Since we're on topic.

Assuming I have an input:

input CreateActivityInput { id: ID createdAt: String updatedAt: String model: String! modelId: ID! owner: String title: String data: String }

And I try to create an activity with a "foo" field - right now the request errors without creating the activity since "foo" was not a valid input field.

Is that the expected behavior?

Likewise, imagine only an "Admins" could set the "data" property above.

Right now, we see an error if a non "Admins" tries to set that property (instead of making the update and ignoring that property).

Is that the expected behavior?

I imagine the answer is "yes" in both cases, but this issue has me questioning myself.

Hi @coderbyheart

Have you tried without the useQuery hook? I suspect it might not be working correctly with partial data (try e.g. with plain client.query(...) or the graphql(...) HOC)

I have tried it with the client directly with the same results:

apolloClient.query({ query: gql(getUser), variables: {id: cognitoUser.username}, fetchPolicy: "network-only" }),

Any joy here? This one is killing us.

What is the error that you're getting? Is it an uncaught promise? If so then you'll need to do something like this:

  async function getData() {
    try {
      const results = await API.graphql(graphqlOperation(listPosts))
      dispatch({ type: 'QUERY', todos: results.data.listPosts.items });
    } catch (err){
      if(err.errors[0].errorType === 'Unauthorized' &&
        (err.errors[0].path.indexOf("ssn") > -1)
      ){
        dispatch({ type: 'QUERY', todos: err.data.listPosts.items });
      }
    }
  }

This assumes I'm doing field level auth on the ssn field, but in general if you're getting an Authorization error which is expected behavior you'll need to catch the promise and then continue on depending on what your business logic dictates (in this case I'm ok with it failing on ssn as that's a partial failure).

my results object is an empty object when there is an error.

As stated above, I'm not using the simplified Amplify GraphQL - but AppSync-suggested Apollo package.

The error is further shown here

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Any action here?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

This issue has been automatically closed because of inactivity. Please open a new issue if are still encountering problems.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

josoroma picture josoroma  Â·  3Comments

guanzo picture guanzo  Â·  3Comments

ddemoll picture ddemoll  Â·  3Comments

rygo6 picture rygo6  Â·  3Comments

shinnapatthesix picture shinnapatthesix  Â·  3Comments