aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.39k stars 2.11k forks source link

Can't use global auth access with non model types #13457

Open cscetbon opened 3 weeks ago

cscetbon commented 3 weeks ago

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

GraphQL API

Amplify Version

v6

Amplify Categories

api

Backend

Amplify CLI

Environment information

``` System: OS: macOS 14.5 CPU: (8) arm64 Apple M1 Pro Memory: 106.28 MB / 16.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 18.14.0 - /usr/local/bin/node Yarn: 1.22.18 - /usr/local/bin/yarn npm: 9.3.1 - /usr/local/bin/npm Browsers: Chrome: 125.0.6422.113 Safari: 17.5 npmPackages: @aws-amplify/ui-react: ^6.1.8 => 6.1.8 @aws-amplify/ui-react-internal: undefined () @chakra-ui/icons: ^2.0.0 => 2.1.1 @chakra-ui/react: ^2.8.2 => 2.8.2 @chakra-ui/theme-tools: ^2.0.0 => 2.1.2 @emotion/cache: ^11.7.1 => 11.11.0 @emotion/react: ^11.4.1 => 11.11.1 @emotion/styled: ^11.3.0 => 11.11.0 @faker-js/faker: ^8.3.1 => 8.3.1 @hypertheme-editor/chakra-ui: ^0.1.5 => 0.1.5 @mapbox/mapbox-gl-style-spec: 13.29.0-dev @testing-library/jest-dom: ^5.14.1 => 5.17.0 @testing-library/react: ^11.2.7 => 11.2.7 @testing-library/user-event: ^12.8.3 => 12.8.3 @turf/turf: ^6.5.0 => 6.5.0 apexcharts: 3.35.2 => 3.35.2 aws-amplify: ^6.1.4 => 6.1.4 aws-amplify/adapter-core: undefined () aws-amplify/analytics: undefined () aws-amplify/analytics/kinesis: undefined () aws-amplify/analytics/kinesis-firehose: undefined () aws-amplify/analytics/personalize: undefined () aws-amplify/analytics/pinpoint: undefined () aws-amplify/api: undefined () aws-amplify/api/server: undefined () aws-amplify/auth: undefined () aws-amplify/auth/cognito: undefined () aws-amplify/auth/cognito/server: undefined () aws-amplify/auth/enable-oauth-listener: undefined () aws-amplify/auth/server: undefined () aws-amplify/data: undefined () aws-amplify/data/server: undefined () aws-amplify/datastore: undefined () aws-amplify/in-app-messaging: undefined () aws-amplify/in-app-messaging/pinpoint: undefined () aws-amplify/push-notifications: undefined () aws-amplify/push-notifications/pinpoint: undefined () aws-amplify/storage: undefined () aws-amplify/storage/s3: undefined () aws-amplify/storage/s3/server: undefined () aws-amplify/storage/server: undefined () aws-amplify/utils: undefined () babel-cli: ^6.26.0 => 6.26.0 babel-preset-es2015: ^6.24.1 => 6.24.1 babel-preset-react: ^6.24.1 => 6.24.1 babel-register: ^6.26.0 => 6.26.0 date-fns: ^2.30.0 => 2.30.0 framer-motion: ^4.1.17 => 4.1.17 gh-pages: ^3.2.3 => 3.2.3 mapbox-gl: ^2.14.1 => 2.15.0 react: ^18.0.0 => 18.3.1 react-apexcharts: 1.4.0 => 1.4.0 react-calendar: ^3.7.0 => 3.9.0 react-custom-scrollbars-2: ^4.2.1 => 4.5.0 react-dom: ^18.0.0 => 18.3.1 react-dropzone: ^12.0.4 => 12.1.0 react-icons: ^4.3.1 => 4.12.0 react-is: ^18.0.0 => 18.2.0 (17.0.2, 16.13.1) react-map-gl: 7.1.6 => 7.1.6 react-router-dom: ^5.3.0 => 5.3.4 react-scripts: 5.0.0 => 5.0.0 react-table: ^7.7.0 => 7.8.0 stylis: ^4.1.1 => 4.3.0 (4.2.0) stylis-plugin-rtl: 2.0.2 => 2.0.2 uuid: ^9.0.1 => 9.0.1 (8.3.2) web-vitals: ^1.1.2 => 1.1.2 npmGlobalPackages: corepack: 0.15.3 ganache: 7.7.4 npm: 9.3.1 solc: 0.8.18 yarn: 1.22.18 ```

Describe the bug

I'm working on a graphql api to interact with an exiting database (aws timestream). The issue I'm facing is that even though I've enabled Global Sandbox Mode, I need to mark as public each field I want to access ... I shouldn't even have to set the type itself as public and if I try I get Types annotated with @auth must also be annotated with @model. Of course I don't want to use @model as I don't want it to use DynamoDB as its storage.

Expected behavior

Using input AMPLIFY { globalAuthRule: AuthRule = { allow: public } } should be sufficient to have full public access (using an api key as Global Sandbox Mode is enabled

Reproduction steps

  1. Add a graphql api (amplify add api)
  2. Add my schema (see Code Snippet)
  3. Add function getMetricRecordsByTimeRange (amplify add function) and use following code
    /**
    * @type {import('@types/aws-lambda').APIGatewayProxyHandler}
    */
    exports.handler = async (event) => {
    return [
        {
            timestamp: event.arguments.startTime
        }
    ]

Code Snippet

input AMPLIFY { globalAuthRule: AuthRule = { allow: public } } # FOR TESTING ONLY!

type MetricRecord  {
  value: Float!
  timestamp: Int! @auth(rules: [{allow: public}]) # it shouldn't be required as it's set globally but fails without !
}

type Query {
  getMetricRecordsByTimeRange(startTime: Int!, endTime: Int!): [MetricRecord!]!
    @auth(rules: [{allow: public}]) # that shouldn't be required either but fails without ! 
    @function(name: "getMetricRecordsByTimeRange-${env}")
}

Log output

``` // Put your logs below this line ```

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

When those rules are set on the function and the timestamp field I can see 2 new functions being created:

Both with a request mapping template looking like this

#set( $isAuthorized = false )
#if( $util.authType() = "API Key Authorization" )
#set( $isAuthorized = true )
#end
#ifC $util.authType() == "User Pool Authorization" )
#end
#if( !$isAuthorized )
  Sutil.unauthorized
#end
...
cscetbon commented 3 weeks ago

So I found #652 and was able to use this as a workaround, however I still think @auth should be supported as well as globalAuthRule

input AMPLIFY { globalAuthRule: AuthRule = { allow: public } } # FOR TESTING ONLY!

type MetricRecord
@aws_api_key
@aws_cognito_user_pools 
# those 2 directives shouldn't be required
# @auth could be used, but the global rule should apply here
{
  value: Float!
  timestamp: Int!
}

type Query {
  getMetricRecordsByTimeRange(startTime: Int!, endTime: Int!): [MetricRecord!]!
    @auth(rules: [{allow: public}]) # that shouldn't be required either but fails without ! 
    @function(name: "getMetricRecordsByTimeRange-${env}")
}

I see no reason why a type that is not a model is treated differently in term of auth support.

chrisbonifacio commented 3 weeks ago

Hi @cscetbon, for custom queries, mutations, and subscriptions, the custom type (or non-model type) in the schema would be protected behind the auth rule on the custom operation. The custom type data isn't backed by a DynamoDB table.

You can also implement a custom auth lambda to restrict access to data and/or fields if you need to have fine grained control over them.

I will label this as a feature request for the team to consider.

cscetbon commented 3 weeks ago

The custom type data isn't backed by a DynamoDB table.

Yeah I understand that and that's what I want

You can also implement a custom auth lambda to restrict access to data and/or fields if you need to have fine grained control over them.

I'm curious, any examples ? In my case I just want to use Cognito (Or an Api Key in test mode) so if it can be automatically supported it would be way better/simpler/consistent. Thanks for the feature-request label !

cscetbon commented 2 weeks ago

@chrisbonifacio 👆

chrisbonifacio commented 2 weeks ago

@cscetbon Here are the Gen 1 docs for setting up a custom auth rule and lambda using either the Amplify CLI or AWS CDK:

https://docs.amplify.aws/gen1/react/build-a-backend/graphqlapi/customize-authorization-rules/#custom-authorization-rule

I think you should be able to grab the user's identity from the event.context.identity in the Lambda function if there's an authorization header with a Cognito token.

If not, then you'll have to verify the token manually and parse the payload to implement your custom logic to determine if the user isAuthorized in the response