Amplify-cli: Prevent public read access for field

Created on 26 May 2020  路  12Comments  路  Source: aws-amplify/amplify-cli

Which Category is your question related to?
API (GraphQL), Auth

Amplify CLI Version
4.18.0

What AWS Services are you utilizing?
AppSync, Cognito, IAM

Provide additional details e.g. code snippets
I created an API that has both Cognito User Pools and IAM authentication. I want unauthenticated users to read a majority of the fields on the book model (e.g. title, description, etc.) but I want some fields to be limited to authenticated users (e.g. chapters).

However, since the providers are different (Cognito vs. IAM) the unauthenticated users are able to read every field regardless of group field level auth rules.

type Book
  @model
  @searchable
  @auth(
    rules: [
      {allow: public, provider: iam, operations: [read]},
      {allow: groups, groups: ["Customer"], operations: [read]},
      {allow: groups, groups: ["Admin"], operations: [create, read, update, delete]}
    ]
  )
{
  id: ID!
  title: String!
  description: String!
  chapters: [Chapter!] # <--- Chapters should be restricted to only authenticated users
    @auth(rules: [
      {allow: groups, groups: ["Customer"], operations: [read]},
      {allow: groups, groups: ["Admin"], operations: [create, read, update, delete]}
    ])
}

How can I prevent public read access for certain fields while allowing it for other fields?

@auth pending-close-response-required pending-response question

Most helpful comment

@edwardfoyle @ammarkarachi Does anyone know what could be the issue here? This has been a problem for me for two weeks now. Thanks

All 12 comments

@TheBenck Since the auth rules are applied from Model then to field you would have to specify for each field explicitly

type Book
  @model
  @searchable
  @auth(
    rules: [
      {allow: groups, groups: ["Customer"], operations: [read]},
      {allow: groups, groups: ["Admin"], operations: [create, read, update, delete]}
    ]
  )
{
  id: ID! @auth(rules: [{allow: public, provider: iam, operations: [read]} ])
  title: String! @auth(rules: [{allow: public, provider: iam, operations: [read]} ])
  description: String! @auth(rules: [{allow: public, provider: iam, operations: [read]} ])
  chapters: [Chapter!] 
}

Thanks @ammarkarachi. Would that mean I can't have subscriptions? I get this error on compilation:

Per-field auth on the required field id is not supported with subscriptions.
Either make the field optional, set auth on the object and not the field, or disable subscriptions for the object (setting level to off or public)

@TheBenck there are alternatives to using subscriptions with as outlined here

Thanks @ammarkarachi , that got me further. I'm coming across another strange problem with this case though.

I have similar rules for a couponCode field. Only dynamic groups and the Admin group should be authorized to view it. However, it's visible to unauthenticated IAM users as well even though there's no specific rule for it.

Why could this be happening? It seems like scalars cannot be protected this way like the Chapter type could? I've seen this done in the docs though.

type Book
  @model(subscriptions: {level: public})
  @searchable
  @auth(
    rules: [
      {allow: groups, groups: ["Customer"], operations: [read]},
      {allow: groups, groups: ["Admin"], operations: [create, read, update, delete]}
    ]
  )
{
  id: ID!
    @auth(rules: [
      {allow: public, provider: iam, operations: [read]},
      {allow: private, provider: userPools, operations: [read]}
    ])

  # ...

  groupsCanAccess: [String]!
    @auth(rules: [
      {allow: public, provider: iam, operations: [read]},
      {allow: private, provider: userPools, operations: [read]}
    ])
  couponCode: String
    @auth(rules: [
      {allow: groups, groupsField: "groupsCanAccess", operations: [read]},
      {allow: groups, groups: ["Admin"], operations: [create, read, update, delete]}
  ])
}

Also, is purely field-level auth incompatible with @searchable?

@edwardfoyle Any update on these issues? Thanks

@edwardfoyle @ammarkarachi Does anyone know what could be the issue here? This has been a problem for me for two weeks now. Thanks

@edwardfoyle @ammarkarachi
I am having exactly same issue, for example, let's say I have a model like this:

type User @model @auth(rules: [
    { allow: owner, identityClaim: "sub", operations: [create, update, delete]},
    { allow: private, provider: iam },
  ]) {
  id: ID!
  nickname: String!
  phoneNumber: String!
  profilePicture: S3Object
  backgroundPicture: S3Object
  owner: String!
  updatedAt: AWSDateTime
  createdAt: AWSDateTime
}

running

await API.graphql({
        query: listUsers,
        variables: {},
        authMode: "AWS_IAM",
      });

would return absolutely nothing as expected, however if I just allow one field in the model to be accessible publicly like so:

type User @model @auth(rules: [
    { allow: owner, identityClaim: "sub", operations: [create, update, delete]},
    { allow: private, provider: iam },
  ]) {
  id: ID!
  nickname: String!
  phoneNumber: String!
  profilePicture: S3Object @auth(rules: [{ allow: public, provider: iam, operations: [read] }])
  backgroundPicture: S3Object
  owner: String!
  updatedAt: AWSDateTime
  createdAt: AWSDateTime
}

suddenly all fields are now available publicly, why?

@edwardfoyle @ammarkarachi
this works:

type Thing 
  @model 
  @auth(rules: [
    { allow: owner, identityClaim: "sub", operations: [create, update, delete]},
    { allow: public, provider: iam, operations: [read] }
  ]) {
    id: ID! 
    nickname: String @auth(rules: [{ allow: owner }])
    phoneNumber: String @auth(rules: [{ allow: owner }])
    profilePicture: String!
    backgroundPicture: String!
}

However, if you replace the field level auth

nickname: String @auth(rules: [{ allow: private, provider: iam }])
phoneNumber: String @auth(rules: [{ allow: private, provider: iam }])

then the Thing.nickname.req mapping template doesn't have any auth around it.

If you look at amplify/backend/api/${PROJECT_NAME}/build/cloudformation-template.json
There is unAuthRoleName and authRoleName
The UnauthRolePolicy01 should not include access
To the

"typeName": "Thing",
"fieldName": "nickname"

@TheBenck @codepreneur
You could add @aws_cognito_user_pools this way the field chapters is only accessible by user pool auth

type Book
  @model
  @searchable
  @auth(
    rules: [
      {allow: groups, groups: ["Customer"], operations: [read]},
      {allow: groups, groups: ["Admin"], operations: [create, read, update, delete]}
    ]
  )
{
  id: ID! @auth(rules: [{allow: public, provider: iam, operations: [read]} ])
  title: String! @auth(rules: [{allow: public, provider: iam, operations: [read]} ])
  description: String! @auth(rules: [{allow: public, provider: iam, operations: [read]} ])
  chapters: [Chapter!] @aws_cognito_user_pools
}

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

Just want to let you know that I found a solution in #5556

Was this page helpful?
0 / 5 - 0 ratings