I have the following Schema:
input CreateEventInput {
userID: String!
eventID: ID!
type: String!
data: String
dateTime: AWSDateTime!
}
type Mutation {
createEvent(input: CreateEventInput!): event
}
type Subscription {
onCreateEvent(): event
@aws_subscribe(mutations: ["createEvent"])
The createEvent
resolver sets the userID
like:
"key" : {
"userID" : $util.dynamodb.toDynamoDBJson($context.identity.username),
"eventID" : $util.dynamodb.toDynamoDBJson($util.autoId())
}
I'd like to limit the subscription so that only records where the userID = $context.identity.username
are returned to the user.
Does anyone know how to set this up? I think I need a resolver on the subscription, but I can't find a clear example of this where you have a Primary partition key (userID
) and Primary sort key (eventID
).
I would really appreciate any help or guidance. I can change the schema or DB if needed.
Update:
I believe I can set the Subscription Response Mapping Template to something like:
#if(${context.identity.username} != ${context.arguments.userID})
$utils.unauthorized()
#else
##User is authorized, but we return null to continue
null
#end
However, I'm at a loss what to put in the Request Mapping Template.
I think the first step to filter subscriptions based on user is easiest done with a minor update to your schema, by breaking the 'input' shape into individual inputs to the mutation. Specifically:
type mutation {
createEvent(userID: String!, eventID: ID!, type: String!,
data: String, dateTime: AWSDateTime!): event
}
... other stuff...
type Subscription {
onCreateEvent(userId: String!): event
@aws_subscribe(mutations: ["createEvent"])
}
A few notes on this:
1) This assumes you want that to be a requirement on the subscription. If not, if you want it to be an optional rule, remove the !. By your comment, I believe you would want it.
2) Subscription filters (which is what the userId parameter is in the subscription operation) require that the filters be in the response for the mutation. So be sure that when you're defining the operation on your client, you include userId in the response there.
3) This is required to apply the subscription filter. The service won't know what a userId is unless it's a direct input to the mutation, having it inside and input shape won't work.
Now, as far as nailing down that a user can't just subscribe to someone else's username. I believe you were looking at this docs page. This will work, is totally valid, and could be completed with the something close to the example in that docs page, but it's based around having a permissions lookup table and a Dynamo resolver. If you don't have one or would prefer to avoid using one, a little adjustment should be able to make it work with a none/local resolver. Without a permissions table or anything to check against, I'd strongly recommend a local/none resolver.
Specifically, I believe you could move what you have in the response mapping template to your new none/local resolver's mapping template...
#if(${context.identity.username} != ${context.arguments.userID})
$utils.unauthorized()
#else
##User is authorized, but we return null to continue
null
#end
...and have the response mapping template be a default response, then you'd have it without unnecessary infrastructure in a permissions table or dead code that sets up a dynamo interaction that does not happen. Instead, all this will do is check the username in the input against the username in the Cognito token.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With