amplify-category-api icon indicating copy to clipboard operation
amplify-category-api copied to clipboard

Can't set field-level authorization in amplify gen2

Open binarycombinatrix opened this issue 1 year ago • 3 comments

Environment information

System:
  OS: Windows 10 10.0.19045
  CPU: (8) x64 Intel(R) Core(TM) i5-8300H CPU @ 2.30GHz
  Memory: 1.06 GB / 7.86 GB
Binaries:
  Node: 20.16.0 - C:\Program Files\nodejs\node.EXE   
  Yarn: 1.22.10 - ~\AppData\Roaming\npm\yarn.CMD     
  npm: 10.8.1 - C:\Program Files\nodejs\npm.CMD      
  pnpm: undefined - undefined
NPM Packages:
  @aws-amplify/backend: 1.0.4
  @aws-amplify/backend-cli: 1.1.1
  aws-amplify: 6.5.0
  aws-cdk: 2.149.0
  aws-cdk-lib: 2.149.0
  typescript: 5.5.3
AWS environment variables:
  AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
  AWS_SDK_LOAD_CONFIG = 1
  AWS_STS_REGIONAL_ENDPOINTS = regional
No CDK environment variables

Description

Even after specifying field level authorization for all required fields, it says required fields missing field-level authorization rules: below is the schema file amplify/data/resource.ts

import { type ClientSchema, a, defineData } from "@aws-amplify/backend";

const schema = a.schema({
  Comment: a.customType({
    content: a
      .string()
      .required()
      .authorization((allow) => [
        allow.publicApiKey().to(["read"]),
        allow.authenticated(),
      ]),
    username: a
      .string()
      .required()
      .authorization((allow) => [
        allow.publicApiKey().to(["read"]),
        allow.authenticated(),
      ]),
    dp: a
      .string()
      .required()
      .authorization((allow) => [
        allow.publicApiKey().to(["read"]),
        allow.authenticated(),
      ]),
    dn: a
      .string()
      .required()
      .authorization((allow) => [
        allow.publicApiKey().to(["read"]),
        allow.authenticated(),
      ]),
  }),
  Video: a
    .model({
      partitionKey: a
        .string()
        .required()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.owner(),
        ]),
      sortKey: a
        .string()
        .required()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.owner(),
        ]), ////video title or username   + uuidv1
      type: a
        .string()
        .required()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.owner(),
        ]), ///specify type to avoid confusion
      category: a.string(), /// category which is partition key for video entry
      debate: a.json().array(), ///debate of the video
      description: a.string(), ///channel or video description
      url: a.string(), ///video url
      thumbnail: a.string(), ///video thumbnail
      dp: a.string(), ///user dp can store in both cases,
      comment: a
        .ref("Comment")
        .array()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.authenticated(),
        ]), ///only in case of video
      dn: a.string(), //display name
      username: a.string(),
    })
    .identifier(["partitionKey", "sortKey"])
    .authorization((allow) => [
      // allow.publicApiKey(),
      allow.publicApiKey().to(["read"]),
      // Allow signed-in user to create, read, update,
      // and delete their __OWN__ posts.
      allow.owner(),
    ]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: "apiKey",
    // API Key is used for a.allow.public() rules
    apiKeyAuthorizationMode: {
      expiresInDays: 30,
    },
  },
});

The error from the terminal is as below:

Failed to instantiate data construct

Caused By: When using field-level authorization rules you need to add rules to all of the model's required fields with at least read permissions. Found model "Video" with required fields ["partitionKey","sortKey","type"] missing field-level authorization rules.\n\nFor more information visit https://docs.amplify.aws/ction-rules

binarycombinatrix avatar Aug 18 '24 09:08 binarycombinatrix

Hey,👋 thanks for raising this! I'm going to transfer this over to our API repository for better assistance 🙂

ykethan avatar Aug 19 '24 15:08 ykethan

Hey @binarycombinatrix, When using field-level authorization, you must grant at least read permissions to the remaining fields. I've noticed that the category, debate, description, url, thumbnail, dp, dn, and username fields in the Video model are missing permissions. Please modify the fields as shown below and let me know if this resolves your issue.

Video: a
    .model({
      partitionKey: a
        .string()
        .required()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.owner(),
        ]),
      sortKey: a
        .string()
        .required()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.owner(),
        ]), ////video title or username   + uuidv1
      type: a
        .string()
        .required()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.owner(),
        ]), ///specify type to avoid confusion
      category: a
        .string()
        .authorization((allow) => [allow.publicApiKey().to(["read"])]),
      debate: a
        .json()
        .array()
        .authorization((allow) => [allow.publicApiKey().to(["read"])]), ///debate of the video
      description: a
        .string()
        .authorization((allow) => [allow.publicApiKey().to(["read"])]), ///channel or video description
      url: a
        .string()
        .authorization((allow) => [allow.publicApiKey().to(["read"])]), ///video url
      thumbnail: a
        .string()
        .authorization((allow) => [allow.publicApiKey().to(["read"])]), ///video thumbnail
      dp: a
        .string()
        .authorization((allow) => [allow.publicApiKey().to(["read"])]), ///user dp can store in both cases,
      comment: a
        .ref("Comment")
        .array()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.authenticated(),
        ]), ///only in case of video
      dn: a
        .string()
        .authorization((allow) => [allow.publicApiKey().to(["read"])]), //display name
      username: a
        .string()
        .authorization((allow) => [allow.publicApiKey().to(["read"])]),
    })
    .identifier(["partitionKey", "sortKey"])
    .authorization((allow) => [
      // allow.publicApiKey(),
      allow.publicApiKey().to(["read"]),
      // Allow signed-in user to create, read, update,
      // and delete their __OWN__ posts.
      allow.owner(),
    ]),

AnilMaktala avatar Aug 22 '24 20:08 AnilMaktala

Hi @AnilMaktala did you try running your version? Those other fields are not required fields and so do not require field level auth, as the model level authorization applies to them and I added publicApi read to it already.

The error too says the issue is with required fields.

binarycombinatrix avatar Aug 25 '24 18:08 binarycombinatrix

I believe there is a bug with the error message. The error message is present when including allow.authenticated() on the comment field.

comment: a
  .ref("Comment")
  .authorization((allow) => [
    allow.authenticated(),
  ]),

I'm not sure why this is causing this error, but you may want to change this anyway. This auth configuration allows any user to modify the comments associated with a video (even videos they do not own). That means any authenticated user could remove comments from a video.

I assume you set this auth so that any user could leave a comment on a given video. A more secure way to implement this would be to create a custom mutation (such as createCommentOnVideo). This would allow authenticated users to add comments to a video, but not remove them.

https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/

dpilch avatar Sep 30 '24 21:09 dpilch

Hey @binarycombinatrix, After further investigation, we found that the comment model has publicApiKey and authenticated auth settings. To resolve the issue, you need to grant access to authenticated users for the required fields as well. Below is the updated schema. Please try it out and let me know the results.

import {
  type ClientSchema,
  a,
  defineData,
  defineFunction,
} from "@aws-amplify/backend";

//const echoHandler = defineFunction({ entry: "./echo-handler/handler.ts" });

const schema = a.schema({
  Comment: a.customType({
    content: a
      .string()
      .required()
      .authorization((allow) => [
        allow.publicApiKey().to(["read"]),
        allow.authenticated(),
      ]),
    username: a
      .string()
      .required()
      .authorization((allow) => [
        allow.publicApiKey().to(["read"]),
        allow.authenticated(),
      ]),
    dp: a
      .string()
      .required()
      .authorization((allow) => [
        allow.publicApiKey().to(["read"]),
        allow.authenticated(),
      ]),
    dn: a
      .string()
      .required()
      .authorization((allow) => [
        allow.publicApiKey().to(["read"]),
        allow.authenticated(),
      ]),
  }),
  Video: a
    .model({
      partitionKey: a
        .string()
        .required()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.owner(),
          allow.authenticated(),
        ]),
      sortKey: a
        .string()
        .required()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.owner(),
          allow.authenticated(),
        ]), ////video title or username   + uuidv1
      type: a
        .string()
        .required()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.owner(),
          allow.authenticated(),
        ]), ///specify type to avoid confusion
      category: a.string(), /// category which is partition key for video entry
      debate: a.json().array(), ///debate of the video
      description: a.string(), ///channel or video description
      url: a.string(), ///video url
      thumbnail: a.string(), ///video thumbnail
      dp: a.string(), ///user dp can store in both cases,
      comment: a
        .ref("Comment")
        .array()
        .authorization((allow) => [
          allow.publicApiKey().to(["read"]),
          allow.authenticated(),
        ]), ///only in case of video
      dn: a.string(), //display name
      username: a.string(),
    })
    .identifier(["partitionKey", "sortKey"])
    .authorization((allow) => [
      // allow.publicApiKey(),
      allow.publicApiKey().to(["read"]),
      // Allow signed-in user to create, read, update,
      // and delete their __OWN__ posts.
      allow.owner(),
    ]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: "apiKey",
    // API Key is used for a.allow.public() rules
    apiKeyAuthorizationMode: {
      expiresInDays: 30,
    },
  },
});

AnilMaktala avatar Oct 31 '24 13:10 AnilMaktala

Hey 👋 , This issue is being closed due to inactivity. If you are still experiencing the same problem and need further assistance, please feel free to leave a comment. This will enable us to reopen the issue and provide you with the necessary support.

AnilMaktala avatar Nov 11 '24 14:11 AnilMaktala

This issue is now closed. Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one.

github-actions[bot] avatar Nov 11 '24 14:11 github-actions[bot]