Amplify-cli: Multi Auth Subscriptions

Created on 15 Jun 2020  路  3Comments  路  Source: aws-amplify/amplify-cli

Describe the bug
I have this model

type Order @model
@key(name: "ByCustomerByDate", fields: ["customer","createdAt"],queryField:"ordersByCustomerByDate")
@key(name: "ByOwnerByStatus", fields: ["owner","status"],queryField:"ordersByOwnerByStatus")
@auth (
    rules: [
        { allow: owner, operations: [read,update,create] },
        { allow: owner, ownerField: "customer", operations: [read] },
        { allow: private, provider: iam, operations: [create,update] },
    ]
){
  id: ID! 
  createdAt: String
  updatedAt: String
  customer: String
  owner: String!
  totalPrice: Float
  status: String!
}  

I want the owner of the model to be able to subscribe to updates on the model and I want the customer to be able to read the model. The problem is when I add two owner auth rules with read access it creates a resolver and subscription that requires me to pass the ID of an owner and a customer who will never be the same person.

export const onUpdateOrder = /* GraphQL */ `
  subscription OnUpdateOrder($owner: String!, $customer: String!) {
    onUpdateOrder(owner: $owner, customer: $customer) {
      id

## [Start] Determine request authentication mode **
#if( $util.isNullOrEmpty($authMode) && !$util.isNull($ctx.identity) && !$util.isNull($ctx.identity.sub) && !$util.isNull($ctx.identity.issuer) && !$util.isNull($ctx.identity.username) && !$util.isNull($ctx.identity.claims) && !$util.isNull($ctx.identity.sourceIp) && !$util.isNull($ctx.identity.defaultAuthStrategy) )
  #set( $authMode = "userPools" )
#end
## [End] Determine request authentication mode **
## [Start] Check authMode and execute owner/group checks **
#if( $authMode == "userPools" )
  ## No Static Group Authorization Rules **


  ## [Start] Owner Authorization Checks **
  #set( $isOwnerAuthorized = false )
  ## Authorization rule: { allow: owner, ownerField: "owner", identityClaim: "cognito:username" } **
  #set( $allowedOwners0 = $util.defaultIfNull($ctx.args.owner, null) )
  #set( $identityValue = $util.defaultIfNull($ctx.identity.claims.get("username"),
                        $util.defaultIfNull($ctx.identity.claims.get("cognito:username"), "___xamznone____")) )
  #if( $util.isList($allowedOwners0) )
    #foreach( $allowedOwner in $allowedOwners0 )
      #if( $allowedOwner == $identityValue )
        #set( $isOwnerAuthorized = true )
      #end
    #end
  #end
  #if( $util.isString($allowedOwners0) )
    #if( $allowedOwners0 == $identityValue )
      #set( $isOwnerAuthorized = true )
    #end
  #end
  ## Authorization rule: { allow: owner, ownerField: "customer", identityClaim: "cognito:username" } **
  #set( $allowedOwners1 = $util.defaultIfNull($ctx.args.customer, null) )
  #set( $identityValue = $util.defaultIfNull($ctx.identity.claims.get("username"),
                        $util.defaultIfNull($ctx.identity.claims.get("cognito:username"), "___xamznone____")) )
  #if( $util.isList($allowedOwners1) )
    #foreach( $allowedOwner in $allowedOwners1 )
      #if( $allowedOwner == $identityValue )
        #set( $isOwnerAuthorized = true )
      #end
    #end
  #end
  #if( $util.isString($allowedOwners1) )
    #if( $allowedOwners1 == $identityValue )
      #set( $isOwnerAuthorized = true )
    #end
  #end
  ## [End] Owner Authorization Checks **


  ## [Start] Throw if unauthorized **
  #if( !($isStaticGroupAuthorized == true || $isOwnerAuthorized == true) )
    $util.unauthorized()
  #end
  ## [End] Throw if unauthorized **
#end
## [End] Check authMode and execute owner/group checks **

$util.toJson(null)

When I remove the customer auth rule. The owner can get the subscriptions on there owned models (updateds are made via a lambda) however this stops the customer from be able to read their orders with a normal query.

Amplify CLI Version
4.21.3

To Reproduce
Create a model with 2 owner rules. Give both read access. Create an instance of the model where the owner A is different to owner B. Look at the subscription made via codegen and it is asking for both owner to be passed. If you only pass in one owner you get an error when the subscription is first made.
image

I tried passing in the customer and a dummy customer to see if the logic worked on an OR logic but it looks like the resolver is to set expect customer AND owner are passed correctly and match what is in the model.

Expected behavior
The different owners should be able to subscribe to the model independently of each other otherwise neither would get a subscription update if they were not the same person.

Desktop (please complete the following information):

  • OS: Windows
  • Node Version. v12.16.0
graphql-transformer question

Most helpful comment

@magjack
You could create a subscription for each owner/business and customer to have real time updates. If you want to keep the auth logic you can create custom resolvers for those operations as well


type Subscription {
  onCreateOrder(owner: String!): Order @aws_subscribe(mutations: ["createOrder"])
  onCreateOrder(customer: String!): Order @aws_subscribe(mutations: ["createOrder"])
}

All 3 comments

@magjack Looking at your use case it is not something the CLI supports out of the box and it cannot automatically done since in CRUD you've 1 update mutation used by two different class of users. In a real world app usually different things can be modified by a customer and the creator of the order (for example one cannot overwrite the others' field, a creator of the owner cannot change the ownership of the order to a different customer).

The reason why we cannot automatically generate code to require only a single owner field is that then how would you decide during the creation of the subscription on which field should you match against the passed in user id? the owner or the customer of the order? There is no way to express it in the schema.

Is that a requirement that both owners must get realtime notifications about the Order changes?

So for my given situation. The owner is a business who takes in orders created by the customers. The orders are created by a lambda function so extra validation can be done and the order is created and then updated in the same execution. The business owner needs to subscribe for new orders. In my use the customer doesn't need to subscribe but only to make queries on the model.

My understanding is the cli, when there is 2 owner auth rules that have read access, it creates a resolver and query where you need to pass in the user id of both an owner and a customer to subscribe to get updates and they both need to be valid. However the customer and business owner will never be the same person so i would never get any updates.

Could the cli not create seperate subscriptions for when you are subscribing as an owner or a customer. Or could the resolver not check for valid ownership of either fields and if one is good then grant the subscription. Currently if I don't pass in a owner and a customer I get an error if one is missing. Can there not be logic to check if one is missing then use the other to check for ownership.

Also I am not currently implementing realtime updates on orders for both customer and business owner (just the business owner currently), however, I feel it would be good if it were possible as it is information both parties would like.

@magjack
You could create a subscription for each owner/business and customer to have real time updates. If you want to keep the auth logic you can create custom resolvers for those operations as well


type Subscription {
  onCreateOrder(owner: String!): Order @aws_subscribe(mutations: ["createOrder"])
  onCreateOrder(customer: String!): Order @aws_subscribe(mutations: ["createOrder"])
}

Was this page helpful?
0 / 5 - 0 ratings