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 77 forks source link

EventBridge example with OrderStatusChange assumes no owner? #2740

Closed OperationalFallacy closed 2 months ago

OperationalFallacy commented 2 months ago

Environment information

npx ampx info
System:
  OS: macOS
Binaries:
  Node: 20.15.0 - ~/.n/bin/node
  Yarn: 4.3.1 - ~/.n/bin/yarn
  npm: 10.7.0 - ~/.n/bin/npm
  pnpm: undefined - undefined
NPM Packages:
  @aws-amplify/backend: 1.0.4
  @aws-amplify/backend-cli: 1.2.0
  aws-amplify: 6.4.0
  aws-cdk: 2.149.0
  aws-cdk-lib: 2.149.0
  typescript: 5.5.3

Description

I'm trying to understand if Amplify gen2 EventBridge example can apply to a case of multiple users subscribe to personal chats with updates streaming. I think this should be close enough order status updates, no?

So instead of .authorization((allow) => [allow.publicApiKey()]) I'd like to have normal .authorization((allow) => [allow.owner().to(["read"])])

for the subscription

  onOrderFromEventBridge: a
    .subscription()
    .for(a.ref("publishOrderFromEventBridge"))
    .authorization((allow) => [allow.publicApiKey()])
    .handler(
      a.handler.custom({
        entry: "./onOrderFromEventBridge.js",
      })
    ),

But it seems impossible? Even though normal ddb-backed models obviously have this capability.

Can you elaborate on the authorization methods for per-owner subscriptions with EventBridge? Does the order status example assumes any user can subscribe to updates on any order?

Thank you!

ykethan commented 2 months ago

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

chrisbonifacio commented 2 months ago

Hi @OperationalFallacy 👋 you should be able to implement a subscription filter that only returns records that match the current user's id like the following:

import { util, extensions } from "@aws-appsync/utils";

// Subscription handlers must return a `null` payload on the request
export function request() {
  return { payload: null };
}

export function response(ctx) {
  const { sub, username } = ctx.identity

  const filter = {
    owner: {
      eq: `${sub}::${username}`,
    },
  };

  extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

  return null;
}
OperationalFallacy commented 2 months ago

Thank you, @chrisbonifacio I figured it out and did exactly like in your example - worked perfectly.

To make this work, custom mutation lambda needs access to graphql, and there is a problem with guest auth, instead of public key here .authorization((allow) => [allow.publicApiKey()]) I wrote about it somewhere in the tickets but can't find it right now.

This particular question solved.

github-actions[bot] commented 2 months ago

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.

julianfrank commented 2 months ago

Hi @OperationalFallacy 👋 you should be able to implement a subscription filter that only returns records that match the current user's id like the following:

import { util, extensions } from "@aws-appsync/utils";

// Subscription handlers must return a `null` payload on the request
export function request() {
  return { payload: null };
}

export function response(ctx) {
  const { sub, username } = ctx.identity

  const filter = {
    owner: {
      eq: `${sub}::${username}`,
    },
  };

  extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

  return null;
}

I'm not able to follow this solution. Where exactly do we need to use this code?

chrisbonifacio commented 2 months ago

Hi @OperationalFallacy 👋 you should be able to implement a subscription filter that only returns records that match the current user's id like the following:


import { util, extensions } from "@aws-appsync/utils";

// Subscription handlers must return a `null` payload on the request

export function request() {

  return { payload: null };

}

export function response(ctx) {

  const { sub, username } = ctx.identity

  const filter = {

    owner: {

      eq: `${sub}::${username}`,

    },

  };

  extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

  return null;

}

I'm not able to follow this solution. Where exactly do we need to use this code?

Please refer to this page:

https://docs.amplify.aws/react/build-a-backend/data/custom-subscription/#define-a-custom-subscription

A custom subscription accepts a handler where you provide a path to a .js file.

The code I provided is the AppSync JS resolver that you pass to the handler of a custom subscription in your schema.

OperationalFallacy commented 2 months ago

There is a similar use-case with resolvers in this aws repo:
https://github.com/aws-samples/aws-genai-llm-chatbot/blob/main/lib/chatbot-api/functions/resolvers/lambda-resolver.js

They setup custom appsync with cdk, but the idea is the same