I am trying to setup a graphql API using AWS Amplify which creates a notes system in which any authenticated user can create a note, as well as read any note created by a user that shares a cognito group with them.
I can see in the corresponding dynamoDB that the groups
field is not being set, meaning that my fetch requests are returning an empty list.
I am using amplify gen 1 v6, and have been using these docs as reference, but am stuck on why it's not working and can't find any errors to go off.
I began with the schema:
type Todo @model @auth(rules: [
{ allow: groups, groupsField: "group"}
]) {
id: ID!
text: String!
groups: [String] @function(name: "setGroupField-${env}")
}
but was getting authentication errors when creating, and so changed it to:
type Todo @model @auth(rules: [
{ allow: groups, groupsField: "group", operations: [update, read] }
{ allow: owner, operations: [create] }
]) {
id: ID!
text: String!
groups: [String] @function(name: "setGroupField-${env}")
}
that fixed the authentication error when creating. I have also tried setting groups as a required field, and recieved errors that the create request did not fulfill the schema requirements.
I have the following setGroupField lambda resolver in both cases:
import json
import boto3
def handler(event, context):
# Extract the username and groups from the identity information
identity = event['identity']
groups = identity['claims'].get('cognito:groups', [])
print(groups)
# Check if the user belongs to a group
if not groups:
raise Exception("User is not part of any group.")
return groups
The print statement in the resolver has verified that it is successfully getting the correct groups.
Update:
I have tried using the Amplify generated UI components, which give the user the ability to set the group field (not something I want, but thought it would be worth a try) and it is now setting the group field, but to the user given value, not that returned by the lambda resolver.
I re-read through the new docs and noticed the statement:
While @model automatically generates dedicated "create", "read", "update", "delete", and "subscription" queries or mutations for you, there are some cases where you want to define a stand-alone query or mutation.
Changing my schema and lambda to the following creates the expected behaviour:
type Todo
@model(mutations:
{ create: null, update: null, delete: null }
)
@auth(rules: [
{ allow: groups, groupsField: "group", operations: [read] }
{ allow: owner, operations: [create] }
])
{
id: ID!
text: String!
group: String!
owner: String!
createdAt: AWSDateTime
}
input createTodoInput {
text: String!
group: String!
}
type Mutation {
createTodoSecure(input: createTodoInput): Todo @function(name: "setGroupField-${env}")
}
import json
import boto3
from datetime import datetime
import uuid
import os
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['API_ACTIONSTAKEN_TODOTABLE_NAME'])
def handler(event, context):
# Extract the username and groups from the identity information
identity = event['identity']
groups = identity['claims'].get('cognito:groups', [])
username = identity['username']
print(event)
# Check if the user belongs to a group
if not groups:
raise Exception("User is not part of any group.")
# Extract input fields
input_data = event['arguments']['input']
text = input_data['text']
group = input_data['group']
# Ensure the user is part of the specified group
if group not in groups:
raise Exception("User is not authorized to create a todo in this group.")
# Generate a unique ID for the new todo item
todo_id = str(uuid.uuid4())
# Prepare the item
item = {
'id': todo_id,
'text': text,
'group': group,
'owner': username,
'createdAt': datetime.utcnow().isoformat(timespec='seconds') + 'Z',
'updatedAt': datetime.utcnow().isoformat(timespec='seconds') + 'Z',
'_lastChangedAt': 0,
'_version': 1,
}
# Write the item to DynamoDB
table.put_item(Item=item)
# Return the ID of the created todo item
return item