Amplify get owner user attributes from Cognito - amazon-web-services

Background:
I'm using Amplify to create a type Item #model GraphQL datastore with an #auth allow owner which adds the owner as an CognitoID to the Item.
Question:
This item is readable for guests and I would like to show the owner (or other related metadata such as a username/email) for this item.
e.g. Cognito.GetUserAttributes(2b10fb7e-4472-43c9-9bb9-54219b5027a3)
Is somthing like this possible? How is this supposed to be designed for this use case? Do I have to add a lambda that adds the user meta data to the item when it is created?
I would like this solution to be scalable.
The schema:
type Item
#model
#auth(rules: [
{ allow: owner},
{ allow: groups, groups: ["Admin"] },
{ allow: private, operations: [read] },
{ allow: public, operations: [read] }
])
{
id: ID!
title: String!
description: String
}
From DynamoDB:
{
"__typename": "Item",
"_lastChangedAt": 1593707126605,
"_version": 1,
"createdAt": "2020-07-02T16:25:26.582Z",
"description": "Description",
"id": "0592fcd8-408e-406e-84bf-9f63eb43e147",
"owner": "2b10fb7e-4472-43c9-9bb9-54219b5027a3",
"title": "Item",
"updatedAt": "2020-07-02T16:25:26.582Z"
}

You can use a function resolver on a cognitoAttributes field. The lambda function can call admin-get-user in Cognito and return the attributes. The lambda function will receive the dynamo Item in the event.source property so you will have access to the owner id to make the call to Cognito.
type Item
#model
#auth(rules: [
{ allow: owner},
{ allow: groups, groups: ["Admin"] },
{ allow: private, operations: [read] },
{ allow: public, operations: [read] }
])
{
id: ID!
title: String!
cognitoAttributes: [String]! #function('cognitoGetUserAttributes-${env})
description: String
}

Related

AWS Amplify #auth rule require multiple group membership

Is it possible to have auth rules requiring authenticated users to belong to multiple groups? For example "allow users who are in 'Tenant N' AND who are 'Editors'".
Our models are currently similar to this:
type MyModel
#model
#auth(rules: [
{ allow: owner },
{ allow: groups, groupsField: "tenantID" },
])
{
id: ID!
tenantID: String!
}
So using static group auth doesn't work for us:
type MyModel
#model
#auth(rules: [
{ allow: groups, groups: ["DynamicTenantId", "Editor"] }
])
{
id: ID!
tenantID: String!
}
Because tenantID is a dynamic value, we need to use dynamic group auth instead:
type MyModel
#model
#auth(rules: [
{ allow: owner },
{ allow: groups, groupsField: "tenantID" },
{ allow: groups, group: "Editor" },
])
{
id: ID!
tenantID: String!
}
But this doesn't work because it's an "OR", saying "allow anyone in the tenant OR anyone with the 'Editor' group".
Updating our models to use single dynamic group auth field doesn't work either:
type MyModel
#model
#auth(rules: [
{ allow: owner },
{ allow: groups, groupsField: "allowGroups" },
])
{
id: ID!
allowGroups: [String] # ['DynamicTenantId', 'Editor']
}
Because this an "OR" too, saying "allow anyone in the tenant OR anyone with the 'Editor' group".
Are there any other options, aside from a custom authenticator, to require multiple group membership?
Got an answer / workaround on the aws-amplify GitHub repo:
https://github.com/aws-amplify/amplify-category-api/issues/28

How to specify #auth so that any authenticated AWS user can read the resource

How can I specify any authenticated user can read the resource, which is created by the Admin Group?
type SupportedBrand #model #auth(rules: [
{ allow: groups, groups: ["Admin"], operations: [ read, create, update, delete ] }
])
{
id: ID!
order: Int!
name: String!
enabled: Boolean!
}

How do you prevent updates to the ownerField of an AWS Amplify GraphQL API?

I created a GraphQL API using AWS' Amplify. In the schema, I have a Comment model that looks like this:
type Comment
#auth(rules: [{ allow: owner }, { allow: private, operations: [read] }, { allow: public, operations: [read] }])
#model
#key(name: "byAuthor", fields: ["authorID"])
#key(name: "byPost", fields: ["postID"]) {
content: String!
createdAt: AWSDateTime!
id: ID!
owner: String
postID: ID!
updatedAt: AWSDateTime!
}
This gives the owner permission to create, read, update, and delete, and restricts unauthenticated/authenticated-non-owner users to read-only. This works as expected; however, the owner can update the ownerField's value, essentially attributing the comment to another user...which is a no-no. To prevent this, I tried using field-level permissions (see below); however, that doesn't appear to be stopping the update.
...
owner: String #auth(rules: [{ allow: owner, operations: [ create ]}])
...
Is there something I'm missing? Any help is much appreciated--thank you!
You're very close. Using a field-level #auth directive, you want to explicitly grant the owner the ability to create and read and explicitly deny the world the ability to update and delete. It might look something like:
type Todo
#model
#auth(rules: [{ allow: owner, ownerField: "author" }]) {
id: ID!
name: String!
author: String
#auth(
rules: [
{ allow: groups, groups: ["FORBIDDEN"], operations: [update, delete] }
{ allow: owner, ownerField: "author", operations: [create, read] }
]
)
}
Here, the "FORBIDDEN" group is a non-existent group. Its name can be anything so long as you do not plan to actually create a group by that name in the future. Because no user will ever have the "FORBIDDEN" group claim, any/all update or delete operations on the field will fail.

How do I restrict creation on a GraphQL object using another object's owner field using AWS Amplify #auth tag?

E.g. I mostly have Amplify's pre-generated "Many-to-one" schema, i.e., Blogs with Posts and Comments. I am trying to restrict users from posting (i.e. creating a Post) to another user's Blog. How do I do this?
type Blog
#model
#auth(
rules: [
{ allow: owner, operations: [create, update, read] }
{ allow: private, operations: [read] }
]
)
{
id: ID!
name: String!
posts: [Post]
#connection(keyName: "byBlog", fields: ["id"])
#auth(rules: [{ allow: owner, operations: [create, update, delete] }]) // doesn't work!
}
type Post
#model
#auth(
rules: [
{ allow: owner }
{ allow: private, operations: [read] }
]
)
#key(name: "byBlog", fields: ["blogID"]) {
id: ID!
title: String!
content: String!
blogID: ID!
blog: Blog #connection(fields: ["blogID"])
}
// Comments omitted for brevity
Using the AppSync API, I can create a post under the wrong blog, e.g.,
mutation CreatePostFor22 {
createPost(input: {blogID: "47ce4075-6fcd-433f-xyz", content: "hello world from appsync", title: "some title"}) {
id
owner
blog {
owner // doesn't match the post owner!
}
}
}
This is replicable from my webapp.
Thanks!

AWS Amplify Appsync Model — auth

I have a Team #model with #auth settings. The #model also has an admin field that implies who has CRUD credentials. Here is the model:
type Team
#model
#auth(
rules: [
{ allow: owner, operations: [create, update, delete] }
{ allow: owner, ownerField: "admins", operations: [create, update, delete] }
{ allow: owner, ownerField: "members", operations: [read, update] }
{ allow: owner, ownerField: "viewers", operations: [read] }
]
) {
id: ID!
name: String!
admins: [User!]!
members: [User]
viewers: [User]
teamInfo: TeamInfo!
}
type User #model {
id: ID!
name: String
}
type TeamInfo #model {
id: ID!
info: String
}
I have a few other tables, such as TeamInfo and others that are connected to the Team #model. How would the other tables inherit the Team #auth rules?