Search code examples
graphqlaws-amplifyaws-appsync

How to do field level @auth for bi-directional one-to-many @connection with AppSync GraphQL Transform?


I'm trying to figure out how can you protect at field level a one-to-many @connection with @auth against mutations that shouldn't be allowed. (ie: deny a specific user to run a mutation that will end-up inserting posts as another user.)

Starting with the example for protecting a mutation at the field level: https://aws-amplify.github.io/docs/cli/graphql#field-level-authorization

I tried doing something like this:

type User @model @auth(rules: [{ allow: owner, ownerField: "id" }]) {
  id: ID!
  posts: [Post]
    @connection(name: "UserPosts")
    @auth(rules: [{ allow: owner, ownerField: "id" }])
}

type Post @model {
  title: String!
  user: User!
    @connection(name: "UserPosts")
    @auth(rules: [{ allow: owner, ownerField: "userPostId" }])
}

Then say there already is a user with an id of regular-user-id Apparently my auth rules don't stop another user, say with id of: malicious-user-id to run this mutation:

mutation {
  createPost(input:{
    title:"Oh this is BAD!"
    postUserId: "regular-user-id"
  }) {
    title
  }
}

Running a simple query to make sure this really happened:

query {
  getUser(id:"regular-user-id"){
    posts{
      items
      {
        title
      }
    }
  }
}
=> 
{
  "data": {
    "getUser": {
      "posts": {
        "items": [
          {
            "title": "Regular User title"
          },
          {
            "title": "Oh this is BAD!"
          },
        ]
      }
    }
  }
}

I tried various ways to figure this one out and couldn't find any documentation about bi-directional field level authentication. I'm fairly new to AppSync so I think I must be not getting something, but then this is such common use-case scenario that I'm really surprised there isn't more documentation about it.

Some help would be really appreciated.


Solution

  • To protect the Mutation.createPost mutation such that only the owner of the Post as designated via the postUserId may access it you add an @auth directive to the Post object definition:

    type Post @model @auth(rules: [{ allow: owner, ownerField: "postUserId" }]) {
      title: String!
      # This will use a field 'postUserId' by default.
      user: User!
        @connection(name: "UserPosts")
    }
    

    With this setup, a mutation:

    mutation {
      createPost(input:{
        title:"Oh this is BAD!"
        postUserId: "regular-user-id"
      }) {
        title
      }
    }
    

    will fail if the logged in user is not "regular-user-id".

    This answer may help fill things in as well https://github.com/aws-amplify/amplify-cli/issues/1507#issuecomment-513042021.