aws-amplify / amplify-category-api

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development. This plugin provides functionality for the API category, allowing for the creation and management of GraphQL and REST based backends for your amplify project.
https://docs.amplify.aws/
Apache License 2.0
89 stars 75 forks source link

Inconsistent owner field value #1261

Open adam-nygate opened 1 year ago

adam-nygate commented 1 year ago

How did you install the Amplify CLI?

npm

If applicable, what version of Node.js are you using?

8.3.1

Amplify CLI Version

10.7.2

What operating system are you using?

Mac

Did you make any manual changes to the cloud resources managed by Amplify? Please describe the changes made.

No manual changes made.

Describe the bug

When a Cognito authorised user executes a create mutation, the owner field seemingly randomly is either populated with the user's username or in the format username::username.

I.e: "437349b1-dc44-4b8f-910b-0c0b8d5080c5" or "437349b1-dc44-4b8f-910b-0c0b8d5080c5::437349b1-dc44-4b8f-910b-0c0b8d5080c5"

Expected behavior

That the owner field's value is consistently "437349b1-dc44-4b8f-910b-0c0b8d5080c5" or "437349b1-dc44-4b8f-910b-0c0b8d5080c5::437349b1-dc44-4b8f-910b-0c0b8d5080c5"

Reproduction steps

I'm unsure of how to reproduce this bug (unsure what has suddenly caused it)

Project Identifier

0528e374359df9c027039706af7a73d9

Log output

No error messages etc as the bug is silent

Additional information

No response

Before submitting, please confirm:

josefaidt commented 1 year ago

Hey @adam-nygate :wave: thanks for raising this! The owner values are stored using sub::username which in your case the username is mapped to the sub, which is how usernames are mapped with social auth providers. The resolvers that are generated on amplify api gql-compile will remove the preceding sub:: from the stored owner value, which may be the source of confusion here. Do you see the inconsistency returned using the AppSync queries?

adam-nygate commented 1 year ago

Hi @josefaidt. Thanks for linking the article, definitely answered the question of where this change came from. I noticed the change in a generated graphql mutation resolver but I'm seeing inconsistency in this value being passed on to further field resolutions.

My schema model looks like this:

type Video
  @model(subscriptions: null)
  @auth(
    rules: [
      {
        allow: private
        provider: iam
        operations: [create, read, update, delete]
      }
      {
        allow: owner
        provider: userPools
        operations: [create, read, update, delete]
        ownerField: "ownerId"
      }
      {allow: private, provider: userPools, operations: [create, read]}
    ]
  ) {
  id: ID!
    @auth(
      rules: [
        {allow: private, provider: iam}
        {
          allow: owner
          provider: userPools
          operations: [read]
          ownerField: "ownerId"
        }
        {allow: private, provider: userPools, operations: [read]}
      ]
    )
  ownerId: ID
    @auth(
      rules: [
        {allow: private, provider: iam}
        {
          allow: owner
          provider: userPools
          operations: [read]
          ownerField: "ownerId"
        }
        {allow: private, provider: userPools, operations: [read]}
      ]
    )
  description: String
  recording: S3Object @function(name: "S3ObjectResolver-${env}")
}

type S3Object {
  get: AWSURL
  put: AWSURL
}

and when looking at the logs for the triggered function (S3ObjectResolver), the event object inconsistently contains either:

...
"source": {
        "createdAt": "2023-02-15T20:55:41.825Z",
        "__typename": "Video",
        "id": "c82c7ed9-17c6-4964-b2c9-ff16ae81fd66",
        "ownerId": "437349b1-dc44-4b8f-910b-0c0b8d5080c5::437349b1-dc44-4b8f-910b-0c0b8d5080c5",
        "__operation": "Mutation",
        "updatedAt": "2023-02-15T20:55:41.825Z"
},
...

or

...
"source": {
        "createdAt": "2023-02-15T20:51:32.430Z",
        "__typename": "Video",
        "id": "c27d72f1-b15e-4b03-9fb9-b6451010735d",
        "ownerId": "437349b1-dc44-4b8f-910b-0c0b8d5080c5",
        "__operation": "Mutation",
        "updatedAt": "2023-02-15T20:51:32.430Z"
},
...

Both operations are Mutations, yet one's ownerId field is populated with the new "sub::username" format and the other is populated with just the "username". Any idea why this is occurring or how I might troubleshoot/debug it?

alharris-at commented 1 year ago

Hi @adam-nygate - as you've mentioned you will be seeing some owner fields which include the old 'userName only' model, as well as those which include the new sub::username format. The reason for this is to improve security of the generated records. All generated amplify resolvers should continue to work with EITHER username or sub::username. Information on this migration can be found in docs here https://docs.amplify.aws/cli/migration/identity-claim-changes/

as suggested in the docs, records in either of these cases can be queried using a filter such as

query MyQuery {
  listPrivateNotes(
    filter: {
      or: [{ owner: { contains: "::user1" } }, { owner: { eq: "user1" } }]
    }
  ) {
    nextToken
    items {
      id
      content
    }
  }
}

If you don't wish to have this behavior, you may change your auth rule to explicitly specify the claim type you'd like to persist, e.g. { allow: owner identityClaim: "username" }.

I'm closing this issue as a question, but feel free to reopen if you have issues with the above instructions.

adam-nygate commented 1 year ago

Hi @alharris-at, thanks for your comment but I'm not sure if it addresses the inconsistency at the root of my issue. Please see the two example mutation logs provided, these were executed within minutes of each other, yet the mutation created one record with the old "username" behaviour and one with the new "sub::username" behaviour. I agree that I can override this behaviour by using the "identityClaim" field, but I suppose I'm more so trying to highlight this inconsistency as a bug.

alharris-at commented 1 year ago

I see, thank you for clarifying for me. I'll reopen this issue, mark as pending triage, and we'll see if we can get a repro.

josefaidt commented 1 year ago

Hey @adam-nygate are all of the mutation calls made with Cognito User Pools? Is there a scenario where these records are created using IAM auth and there is some logic passing in the user information?

adam-nygate commented 1 year ago

Hey @josefaidt, nope, all made with Cognito User Pools. I can send the logs for the entire event if that helps?

josefaidt commented 1 year ago

Hey @adam-nygate thanks for clarifying, and sure that may provide some additional insight! Are all of the GraphQL calls made from the same environment? For instance, are all of the mutations being called from the same frontend page in succession? Is the owner information being included in the request as an input variable?

adam-nygate commented 1 year ago

Hey @josefaidt, yes, all calls are coming from the same frontend environment, using the aws-amplify library for auth and graphql operations.

I couldn't find those exact logs buried in my cloudwatch, but found these two events that exhibit the same behaviour. I've redacted any information that I perceive to be sensitive. If you'd like the unredacted events, please let me know a way that I can get them to you privately.

2023-02-18T11:44:03.417Z    0fc2d13c-5972-48c4-be5f-5fd229098594    INFO    EVENT: {
    "typeName": "Video",
    "fieldName": "recording",
    "arguments": {},
    "identity": {
        "claims": {
            "origin_jti": "ab088239-4d73-4939-b2c3-6dc4c9f6e2c4",
            "sub": "437349b1-dc44-4b8f-910b-0c0b8d5080c5",
            "event_id": "f456e352-28da-4fb3-980e-abb4311e9141",
            "token_use": "access",
            "scope": "aws.cognito.signin.user.admin",
            "auth_time": 1676720624,
            "iss": "REDACTED",
            "exp": 1676724224,
            "iat": 1676720624,
            "client_id": "4ga2lfa7onmtnp2agpd57cqvud",
            "jti": "8a058a0e-117d-4033-a9f6-00d002f799cf",
            "username": "437349b1-dc44-4b8f-910b-0c0b8d5080c5"
        },
        "defaultAuthStrategy": "ALLOW",
        "groups": null,
        "issuer": "REDACTED",
        "sourceIp": [
            "REDACTED"
        ],
        "sub": "437349b1-dc44-4b8f-910b-0c0b8d5080c5",
        "username": "437349b1-dc44-4b8f-910b-0c0b8d5080c5"
    },
    "source": {
        "createdAt": "2023-02-18T11:44:02.400Z",
        "__typename": "Video",
        "id": "eb2e8d31-2555-4e2f-b3aa-ef9a8746d21d",
        "ownerId": "437349b1-dc44-4b8f-910b-0c0b8d5080c5::437349b1-dc44-4b8f-910b-0c0b8d5080c5",
        "__operation": "Mutation",
        "updatedAt": "2023-02-18T11:44:02.400Z",
        "status": "pending"
    },
    "request": {
        "headers": {
            "x-forwarded-for": "REDACTED",
            "cloudfront-viewer-country": "GB",
            "cloudfront-is-tablet-viewer": "false",
            "x-amzn-requestid": "395b4a7f-bd5a-4b22-b552-a80b6ef388c2",
            "via": "2.0 0f9abff0779787e38b3d83ae17ff6224.cloudfront.net (CloudFront)",
            "cloudfront-forwarded-proto": "https",
            "origin": "http://localhost:3000",
            "content-length": "153",
            "accept-language": "en-GB,en;q=0.9",
            "host": "REDACTED",
            "x-forwarded-proto": "https",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15",
            "cloudfront-is-mobile-viewer": "false",
            "accept": "application/json, text/plain, */*",
            "cloudfront-viewer-asn": "5378",
            "cloudfront-is-smarttv-viewer": "false",
            "accept-encoding": "gzip, deflate, br",
            "referer": "http://localhost:3000/",
            "content-type": "application/json; charset=UTF-8",
            "x-amz-cf-id": "frshV-2KS8u6DjyR3XUUKAm2XH2leSbuPM9PbPnMV9b24Bs7iFmCHw==",
            "x-amzn-trace-id": "Root=1-63f0ba02-11dadfce64ee624235aab3a5",
            "authorization": "REDACTED",
            "x-amz-user-agent": "aws-amplify/5.0.13 js",
            "cloudfront-is-desktop-viewer": "true",
            "x-forwarded-port": "443"
        },
        "domainName": null
    },
    "prev": {
        "result": {}
    }
}
2023-02-18T11:44:31.878Z    bfdfa16d-cea7-4029-a02d-37697d3b2757    INFO    EVENT: {
    "typeName": "Video",
    "fieldName": "recording",
    "arguments": {},
    "identity": {
        "claims": {
            "origin_jti": "ab088239-4d73-4939-b2c3-6dc4c9f6e2c4",
            "sub": "437349b1-dc44-4b8f-910b-0c0b8d5080c5",
            "event_id": "f456e352-28da-4fb3-980e-abb4311e9141",
            "token_use": "access",
            "scope": "aws.cognito.signin.user.admin",
            "auth_time": 1676720624,
            "iss": "REDACTED",
            "exp": 1676724224,
            "iat": 1676720624,
            "client_id": "4ga2lfa7onmtnp2agpd57cqvud",
            "jti": "8a058a0e-117d-4033-a9f6-00d002f799cf",
            "username": "437349b1-dc44-4b8f-910b-0c0b8d5080c5"
        },
        "defaultAuthStrategy": "ALLOW",
        "groups": null,
        "issuer": "REDACTED",
        "sourceIp": [
            "REDACTED"
        ],
        "sub": "437349b1-dc44-4b8f-910b-0c0b8d5080c5",
        "username": "437349b1-dc44-4b8f-910b-0c0b8d5080c5"
    },
    "source": {
        "createdAt": "2023-02-18T11:44:31.738Z",
        "__typename": "Video",
        "id": "6216d81a-7f38-4f4f-82b7-ee6e3c40d576",
        "ownerId": "437349b1-dc44-4b8f-910b-0c0b8d5080c5",
        "__operation": "Mutation",
        "updatedAt": "2023-02-18T11:44:31.738Z",
        "status": "pending"
    },
    "request": {
        "headers": {
            "x-forwarded-for": "REDACTED",
            "cloudfront-viewer-country": "GB",
            "cloudfront-is-tablet-viewer": "false",
            "x-amzn-requestid": "c7b78d28-51ec-43ce-a50d-50155e966005",
            "pragma": "no-cache",
            "via": "2.0 0f9abff0779787e38b3d83ae17ff6224.cloudfront.net (CloudFront)",
            "cloudfront-forwarded-proto": "https",
            "origin": "http://localhost:3000",
            "content-length": "153",
            "cache-control": "no-cache",
            "x-forwarded-proto": "https",
            "accept-language": "en-GB,en;q=0.9",
            "host": "REDACTED",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15",
            "cloudfront-is-mobile-viewer": "false",
            "accept": "application/json, text/plain, */*",
            "cloudfront-viewer-asn": "5378",
            "cloudfront-is-smarttv-viewer": "false",
            "accept-encoding": "gzip, deflate, br",
            "referer": "http://localhost:3000/",
            "content-type": "application/json; charset=UTF-8",
            "x-amz-cf-id": "XKpE0yPIQ6BSzMBwKNBu0l2JecT1bYQLWkP_W7le_uXloIgVcbSTmA==",
            "x-amzn-trace-id": "Root=1-63f0ba1f-06bb344311c96ec67aedfc19",
            "authorization": "REDACTED",
            "x-amz-user-agent": "aws-amplify/5.0.13 js",
            "cloudfront-is-desktop-viewer": "true",
            "x-forwarded-port": "443"
        },
        "domainName": null
    },
    "prev": {
        "result": {}
    }
}
josefaidt commented 1 year ago

Hey @adam-nygate thanks for clarifying! Are these calls from the same user as well? Is there a difference here between calling as an OAuth'd user and a Cognito user?

adam-nygate commented 1 year ago

Hey @adam-nygate thanks for clarifying! Are these calls from the same user as well? Is there a difference here between calling as an OAuth'd user and a Cognito user?

Yep, both the same user and both were logged in via amplify-js's Auth.SignIn()...

I went through a bunch of logs and was trying to find any indication of a pattern, so far I've checked the following:

josefaidt commented 1 year ago

Hey @adam-nygate :wave: from those logs it appears the ownerId is being returned differently, however looking at a sample auth resolver I am not seeing how this data lines up with the fallback to storing owner as username

"source": {
    "createdAt": "2023-02-18T11:44:02.400Z",
    "__typename": "Video",
    "id": "eb2e8d31-2555-4e2f-b3aa-ef9a8746d21d",
    "ownerId": "437349b1-dc44-4b8f-910b-0c0b8d5080c5::437349b1-dc44-4b8f-910b-0c0b8d5080c5",
    "__operation": "Mutation",
    "updatedAt": "2023-02-18T11:44:02.400Z",
    "status": "pending"
},
"source": {
    "createdAt": "2023-02-18T11:44:31.738Z",
    "__typename": "Video",
    "id": "6216d81a-7f38-4f4f-82b7-ee6e3c40d576",
    "ownerId": "437349b1-dc44-4b8f-910b-0c0b8d5080c5",
    "__operation": "Mutation",
    "updatedAt": "2023-02-18T11:44:31.738Z",
    "status": "pending"
},

sample resolver logic

#if( $util.authType() == "User Pool Authorization" )
  #set( $ownerEntity0 = $util.defaultIfNull($ctx.args.input.owner, null) )
  #set( $ownerClaim0 = $util.defaultIfNull($ctx.identity.claims.get("sub"), null) )
  #set( $currentClaim1 = $util.defaultIfNull($ctx.identity.claims.get("username"), $util.defaultIfNull($ctx.identity.claims.get("cognito:username"), null)) )
  #if( !$util.isNull($ownerClaim0) && !$util.isNull($currentClaim1) )
    #set( $ownerClaim0 = "$ownerClaim0::$currentClaim1" )
    #if( $isAuthorized && $util.isNull($ownerEntity0) && !$ctx.args.input.containsKey("owner") )
      $util.qr($ctx.args.input.put("owner", $ownerClaim0))
    #end
    #if( !$isAuthorized )
      #set( $ownerClaimsList0 = [] )
      $util.qr($ownerClaimsList0.add($util.defaultIfNull($ctx.identity.claims.get("sub"), null)))
      $util.qr($ownerClaimsList0.add($util.defaultIfNull($ctx.identity.claims.get("username"), $util.defaultIfNull($ctx.identity.claims.get("cognito:username"), null))))
      #set( $ownerAllowedFields0 = ["id","name","description","owner","priority","comments","createdAt","updatedAt"] )
      #set( $isAuthorizedOnAllFields0 = true )
      #if( $ownerClaim0 == $ownerEntity0 || $ownerClaimsList0.contains($ownerEntity0) )
        #if( $isAuthorizedOnAllFields0 )
          #set( $isAuthorized = true )
        #else
          $util.qr($allowedFields.addAll($ownerAllowedFields0))
        #end
      #end
      #if( $util.isNull($ownerEntity0) && !$ctx.args.input.containsKey("owner") )
        $util.qr($ctx.args.input.put("owner", $ownerClaim0))
        #if( $isAuthorizedOnAllFields0 )
          #set( $isAuthorized = true )
        #else
          $util.qr($allowedFields.addAll($ownerAllowedFields0))
        #end
      #end
    #end
  #end
#end

I set up a small sample app using the following React code to create a bunch of Todos

import { useState, useEffect, useReducer } from 'react'
import { API } from 'aws-amplify'
import { createTodo } from '../graphql/mutations'
import { faker } from '@faker-js/faker'

async function createFakeTodo() {
  const todo = {
    name: faker.lorem.words(3),
    description: 'Fake todo created by flood',
  }
  let result = {}
  try {
    result = await API.graphql({
      query: createTodo,
      variables: { input: todo },
      authMode: 'AMAZON_COGNITO_USER_POOLS',
    })
  } catch (thrown) {
    // @ts-expect-error api.graphql is goofy
    result = thrown
  }
  return result
}

export default function FloodPage() {
  const [isFlooding, setIsFlooding] = useState(false)
  const [countCreated, setCountCreated] = useState(0)
  const [countFailed, setCountFailed] = useState(0)
  const [log, createLog] = useReducer((log, message) => [...log, message], [])

  async function flood() {
    const result = await createFakeTodo()
    if (result?.data?.createTodo?.id) {
      createLog(`Created todo ${result.data.createTodo.id}`)
      setCountCreated((count) => count + 1)
    } else {
      createLog(`Error: ${result.errors[0]?.message}`)
      setCountFailed((count) => count + 1)
    }
  }

  useEffect(() => {
    console.log({ isFlooding })
    let interval: NodeJS.Timeout
    if (isFlooding) {
      interval = setInterval(flood, 300)
    }
    return () => {
      if (interval) clearInterval(interval)
    }
  }, [isFlooding])

  return (
    <div>
      <button onClick={() => setIsFlooding(true)}>start</button>
      <button onClick={() => setIsFlooding(false)}>stop</button>
      <p>Created: {countCreated}</p>
      <p>Failed: {countFailed}</p>
      <ol style={{ listStyle: 'none' }}>
        {log.map((message, index) => (
          <li
            key={index}
            style={{
              color: message.startsWith('Created') ? 'green' : 'tomato',
            }}
          >
            {message}
          </li>
        ))}
      </ol>
    </div>
  )
}

and noticed events logged in my @function resolver all had the username in the source:

"source": {
    "owner": "03645d1c-5e3e-4a57-896a-2e2461cb8bd8",
    "createdAt": "2023-02-22T22:34:43.536Z",
    "__typename": "Todo",
    "name": "sunt nihil non",
    "description": "Fake todo created by flood",
    "id": "e4ac976a-4b8a-4e06-8b60-7c72ee21a4ee",
    "priority": "LOW",
    "__operation": "Mutation",
    "updatedAt": "2023-02-22T22:34:43.536Z"
},

Combing through DynamoDB records they all appeared to be stored correctly with sub::username.

Are there any additional, slotted resolvers or resolvers that are overridden in this model's pipeline? Or was the ownerField changed after a point in time?

josefaidt commented 1 year ago

As a follow-up question, can you post a snippet of the sample code used to call the mutation? Is the owner information being passed in manually to the input object?

adam-nygate commented 1 year ago

Hey @adam-nygate 👋 from those logs it appears the ownerId is being returned differently, however looking at a sample auth resolver I am not seeing how this data lines up with the fallback to storing owner as username

Hi @josefaidt, I've just made another series of mutations and their is no discernable pattern as to why the VTL is returning the full "sub::username" instead of just one of them. See these 3 event.sources made in relatively quick succession (no changes/deployment occurred between mutations, all via the same frontend, with the same user and means of auth):

"source": {
        "createdAt": "2023-02-23T10:21:04.218Z",
        "__typename": "Video",
        "id": "ed8c597d-fbba-4770-bbe3-82bf47be8f2d",
        "ownerId": "437349b1-dc44-4b8f-910b-0c0b8d5080c5::437349b1-dc44-4b8f-910b-0c0b8d5080c5",
        "__operation": "Mutation",
        "updatedAt": "2023-02-23T10:21:04.218Z",
        "status": "pending"
    },
"source": {
        "createdAt": "2023-02-23T10:24:57.345Z",
        "__typename": "Video",
        "id": "2bfceb35-81d4-4e28-bd22-4ea0d5b12885",
        "ownerId": "437349b1-dc44-4b8f-910b-0c0b8d5080c5",
        "__operation": "Mutation",
        "updatedAt": "2023-02-23T10:24:57.345Z",
        "status": "pending"
    },
"source": {
        "createdAt": "2023-02-23T10:27:48.042Z",
        "__typename": "Video",
        "id": "9ba52a93-4da6-44a1-b3c7-ea4cc41f146a",
        "ownerId": "437349b1-dc44-4b8f-910b-0c0b8d5080c5::437349b1-dc44-4b8f-910b-0c0b8d5080c5",
        "__operation": "Mutation",
        "updatedAt": "2023-02-23T10:27:48.042Z",
        "status": "pending"
    },

Combing through DynamoDB records they all appeared to be stored correctly with sub::username.

In DynamoDB, the records are also correctly stored with the ownerId being "sub::username":

image

Are there any additional, slotted resolvers or resolvers that are overridden in this model's pipeline?

I don't have any overridden VTL resolvers (only the function resolver on the type). Looking in the build/resolvers director, here is the key part of the Mutation.createVideo.auth.1.req.vtl:

#if( $util.authType() == "User Pool Authorization" )
  $util.qr($allowedFields.addAll(["description","recording"]))
  #set( $ownerEntity0 = $util.defaultIfNull($ctx.args.input.ownerId, null) )
  #set( $ownerClaim0 = $util.defaultIfNull($ctx.identity.claims.get("sub"), null) )
  #set( $currentClaim1 = $util.defaultIfNull($ctx.identity.claims.get("username"), $util.defaultIfNull($ctx.identity.claims.get("cognito:username"), null)) )
  #if( !$util.isNull($ownerClaim0) && !$util.isNull($currentClaim1) )
    #set( $ownerClaim0 = "$ownerClaim0::$currentClaim1" )
    #if( $isAuthorized && $util.isNull($ownerEntity0) && !$ctx.args.input.containsKey("ownerId") )
      $util.qr($ctx.args.input.put("ownerId", $ownerClaim0))
    #end
    #if( !$isAuthorized )
      #set( $ownerClaimsList0 = [] )
      $util.qr($ownerClaimsList0.add($util.defaultIfNull($ctx.identity.claims.get("sub"), null)))
      $util.qr($ownerClaimsList0.add($util.defaultIfNull($ctx.identity.claims.get("username"), $util.defaultIfNull($ctx.identity.claims.get("cognito:username"), null))))
      #set( $ownerAllowedFields0 = ["description","status","recording"] )
      #set( $isAuthorizedOnAllFields0 = false )
      #if( $ownerClaim0 == $ownerEntity0 || $ownerClaimsList0.contains($ownerEntity0) )
        #if( $isAuthorizedOnAllFields0 )
          #set( $isAuthorized = true )
        #else
          $util.qr($allowedFields.addAll($ownerAllowedFields0))
        #end
      #end
      #if( $util.isNull($ownerEntity0) && !$ctx.args.input.containsKey("ownerId") )
        $util.qr($ctx.args.input.put("ownerId", $ownerClaim0))
        #if( $isAuthorizedOnAllFields0 )
          #set( $isAuthorized = true )
        #else
          $util.qr($allowedFields.addAll($ownerAllowedFields0))
        #end
      #end
    #end
  #end
#end

Or was the ownerField changed after a point in time?

It's possible that when initially built, there was the default ownerField, but that was changed quickly and these records are being created far after the change of ownerField. Also, there are no items in DDB that use the default ownerField.

adam-nygate commented 1 year ago

As a follow-up question, can you post a snippet of the sample code used to call the mutation? Is the owner information being passed in manually to the input object?

I'm not passing any variable to the mutation, and rely upon an @default to populate a status field and auth rules to populate the ownerId field.

const createVideoMutation = /*GRAPHQL*/ `
  mutation MyMutation {
    createVideo(input: {}) {
      id
      ownerId
      recording {
        get
        put
      }
    }
  }
  `;

  const newVideo = await API.graphql({
    query: createVideoMutation,
  }).catch(console.error);
josefaidt commented 1 year ago

Hey @adam-nygate thanks for the clarifications and for providing those details! We are going to take a further look at this one 🙂