Amplify schema: How can I implement an authorization based on a @connected model’s field?

Background:

This is similar to the model definition from the amplify documentation on the @auth directive:

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

Instead, I’d like to do authorization based on a @connected model’s field, similar to the following:

type Draft @model
  @auth(rules: [
    { allow: owner },
    { allow: owner, ownerField: "editors.id", operations: [update, read] }
  ]) {
  id: ID!
  title: String!
  content: String
  owner: String
  editors: [editor] @connection
}

type editor @model {
  id: ID!
}

Solution:

It seems a workaround by adding a join model (it is called DraftLink in my lab) to encode the relationship between Draft and editor.

Step 1: Create the schema and provision it. Please see details below.

type Draft @model
{
  id: ID!
  title: String!
  content: String
  owner: String
  editors: [DraftLink]  @connection(name: "DraftLink_Draft")
}

type editor @model {
  id: ID!
  drafts:  [DraftLink]  @connection(name: "DraftLink_editor")
}

type DraftLink @model 
@auth(rules: [
  { allow: owner, ownerField: "draftLinkEditorId", operations: [update, read] }
])
{
  id: ID!
  editor: editor! @connection(name: "DraftLink_editor")
  draft: Draft! @connection(name: "DraftLink_Draft")
}

Please note the ownerField “draftLinkEditorId” in DraftLink is generated automatically by Amplify.

Step 2: Add test data including 2 drafts, 3 editors/users and the many-to-many relationship between them (user1->draft1; user2->draft1&draft2; user3->draft2)

# create 2 drafts
mutation createDraft {
    createDraft(input: {
        id: "draft-1"
        title: "Draft 1"
    }) {
        id 
        title
    }
}

mutation createDraft {
    createDraft(input: {
        id: "draft-2"
        title: "Draft 2"
    }) {
        id 
        title
    }
}

# create 3 editors/users
mutation createEditor {
    createEditor(input: {
        id: "user1"
    }) {
        id 
    }
}

mutation createEditor {
    createEditor(input: {
        id: "user2"
    }) {
        id 
    }
}

mutation createEditor {
    createEditor(input: {
        id: "user3"
    }) {
        id 
    }
}

# add relationship [user1->draft1; user2->draft1&draft2; user3->draft2]
mutation createDraftLink {
    createDraftLink(input: {
        draftLinkEditorId: "user1",
        draftLinkDraftId: "draft-1"
    }) {
        id 
    }
}

mutation createDraftLink {
    createDraftLink(input: {
        draftLinkEditorId: "user2",
        draftLinkDraftId: "draft-1"
    }) {
        id 
    }
}

mutation createDraftLink {
    createDraftLink(input: {
        draftLinkEditorId: "user2",
        draftLinkDraftId: "draft-2"
    }) {
        id 
    }
}

mutation createDraftLink {
    createDraftLink(input: {
        draftLinkEditorId: "user3",
        draftLinkDraftId: "draft-2"
    }) {
        id 
    }
}

Step 3: Retrieve data with different users. The query results depend on the query user. For example:

# user1: return draft-1

# user2: return draft-1 and draft-2

# update Auth to user3: return draft-2

query list {
  listDraftLinks {
    items {
      id
      editor {
        id
      }
      draft {
        id
        title
      }
    }
  }
}

I also would like to share this post https://github.com/aws-amplify/amplify-cli/issues/630 for further reference.

No Comments

Leave a Reply