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

authMode: 'AWS_IAM' Permission denied only when user is logged in with Cognito, works if not logged in. Error: Request failed with status code 401 #1087

Closed vincent38wargnier closed 1 year ago

vincent38wargnier commented 1 year ago

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

GraphQL API

Amplify Categories

auth

Environment information

``` # Put output below this line System: OS: macOS 11.5.1 CPU: (8) arm64 Apple M1 Memory: 972.23 MB / 16.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 16.8.0 - /opt/homebrew/bin/node Yarn: 1.22.17 - /opt/homebrew/bin/yarn npm: 7.21.0 - /opt/homebrew/bin/npm Browsers: Chrome: 108.0.5359.98 Safari: 14.1.2 npmPackages: @ampproject/toolbox-optimizer: undefined () @aws-amplify/ui-react: ^4.0.1 => 4.0.1 @aws-amplify/ui-react-internal: undefined () @aws-sdk/client-s3: ^3.224.0 => 3.224.0 (3.6.1) @babel/core: undefined () @babel/runtime: 7.15.4 @edge-runtime/primitives: 2.0.0 @hapi/accept: undefined () @napi-rs/triples: undefined () @next/react-dev-overlay: undefined () @segment/ajv-human-errors: undefined () @vercel/nft: undefined () acorn: undefined () amphtml-validator: undefined () anser: undefined () arg: undefined () assert: undefined () async-retry: undefined () async-sema: undefined () autoprefixer: ^10.4.13 => 10.4.13 aws-amplify: ^5.0.3 => 5.0.3 babel-packages: undefined () browserify-zlib: undefined () browserslist: undefined () buffer: undefined () bytes: undefined () chalk: undefined () ci-info: undefined () cli-select: undefined () comment-json: undefined () compress.js: ^1.2.2 => 1.2.2 compression: undefined () conf: undefined () constants-browserify: undefined () content-disposition: undefined () content-type: undefined () cookie: undefined () cross-spawn: undefined () crypto-browserify: undefined () css.escape: undefined () cssnano-simple: undefined () data-uri-to-buffer: undefined () debug: undefined () devalue: undefined () domain-browser: undefined () edge-runtime: undefined () events: undefined () find-cache-dir: undefined () find-up: undefined () fresh: undefined () get-orientation: undefined () glob: undefined () gzip-size: undefined () http-proxy: undefined () https-browserify: undefined () icss-utils: undefined () ignore-loader: undefined () image-size: undefined () is-animated: undefined () is-docker: undefined () is-wsl: undefined () jest-worker: undefined () json5: undefined () jsonwebtoken: undefined () loader-utils: undefined () lodash.curry: undefined () lru-cache: undefined () micromatch: undefined () mini-css-extract-plugin: undefined () nanoid: undefined () native-url: undefined () neo-async: undefined () next: 13.0.4 => 13.0.4 node-fetch: undefined () node-html-parser: undefined () ora: undefined () os-browserify: undefined () p-limit: undefined () path-browserify: undefined () platform: undefined () postcss: ^8.4.19 => 8.4.19 (8.4.14) postcss-flexbugs-fixes: undefined () postcss-modules-extract-imports: undefined () postcss-modules-local-by-default: undefined () postcss-modules-scope: undefined () postcss-modules-values: undefined () postcss-preset-env: undefined () postcss-safe-parser: undefined () postcss-scss: undefined () postcss-value-parser: undefined () process: undefined () punycode: undefined () querystring-es3: undefined () raw-body: undefined () react: 18.2.0 => 18.2.0 (18.3.0-next-4bd245e9e-20221104) react-beautiful-dnd: ^13.1.1 => 13.1.1 react-dom: 18.2.0 => 18.2.0 (18.3.0-next-4bd245e9e-20221104) react-icons: ^4.6.0 => 4.6.0 react-is: 18.2.0 react-refresh: 0.12.0 react-server-dom-webpack: undefined () react-tooltip: ^4.5.1 => 4.5.1 regenerator-runtime: 0.13.4 sass-loader: undefined () scheduler: undefined () schema-utils: undefined () semver: undefined () send: undefined () setimmediate: undefined () shell-quote: undefined () source-map: undefined () stacktrace-parser: undefined () stream-browserify: undefined () stream-http: undefined () string-hash: undefined () string_decoder: undefined () strip-ansi: undefined () tailwind-merge: ^1.8.0 => 1.8.0 tailwindcss: ^3.2.4 => 3.2.4 tar: undefined () terser: undefined () text-table: undefined () timers-browserify: undefined () tty-browserify: undefined () ua-parser-js: undefined () undici: undefined () unistore: undefined () util: undefined () vm-browserify: undefined () watchpack: undefined () web-vitals: undefined () webpack: undefined () webpack-sources: undefined () ws: undefined () npmGlobalPackages: @aws-amplify/cli: 10.5.1 @ionic/cli: 6.17.1 npm: 7.21.0 yarn: 1.22.17 ```

Describe the bug

Hi, I'm facing an auth issue. I can't make graphQL queries with authMode: 'AWS_IAM' when i'm logged into cognito. Here is my GraphQl configuration :

type Realisation @model
@auth(rules: [
  {allow: owner},
  {allow: public, provider: apiKey},
  {allow: private, operations: [read]}
  {allow: public, operations: [read], provider: iam}
  ]){
  id: ID!
  name: String!
  description: String
  text: String
  position: Int! @index(name: "byPosition", queryField: "getRealisationByPosition")
  status: String! @index(name: "byStatus", queryField: "getRealisationByStatus")
  images: [ImgFile]
} 

I'm trying to list the Realisation elements with this code :

const loadRealisations = async () => {
        const response = await API.graphql({
            query: listRealisations,
            authMode: 'AWS_IAM',
        })
        console.log("REALISATIONS", response.data.listRealisations.items) 
    }

When the user is not logged in, it works ! PROBLEM : when the user is logged in with cognito, I have this error :

image

Expected behavior

When I specify the AuthMode to AWS_IAM, the result should be the same whatever is the Cognito session status

Reproduction steps

then create this table in graphql :

type Realisation @model
@auth(rules: [
  {allow: owner},
  {allow: public, provider: apiKey},
  {allow: private, operations: [read]}
  {allow: public, operations: [read], provider: iam}
  ]){
  id: ID!
  name: String!
  description: String
  text: String
  position: Int! @index(name: "byPosition", queryField: "getRealisationByPosition")
  status: String! @index(name: "byStatus", queryField: "getRealisationByStatus")
  images: [ImgFile]
} 

then amplify push, then create a cognito user, and log in then try to run this code :

await API.graphql({
            query: listRealisations,
            authMode: 'AWS_IAM',
        })

Code Snippet

No response

Log output

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

aws-exports.js

/ eslint-disable / // WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.

const awsmobile = { "aws_project_region": "eu-west-2", "aws_cognito_identity_pool_id": "**", "aws_cognito_region": "eu-west-2", "aws_user_pools_id": "", "aws_user_pools_web_client_id": "", "oauth": {}, "aws_cognito_username_attributes": [ "EMAIL" ], "aws_cognito_social_providers": [], "aws_cognito_signup_attributes": [ "EMAIL" ], "aws_cognito_mfa_configuration": "OFF", "aws_cognito_mfa_types": [ "SMS" ], "aws_cognito_password_protection_settings": { "passwordPolicyMinLength": 8, "passwordPolicyCharacters": [] }, "aws_cognito_verification_mechanisms": [ "EMAIL" ], "aws_appsync_graphqlEndpoint": "https://", "aws_appsync_region": "eu-west-2", "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS", "aws_appsync_apiKey": "*", "aws_user_files_s3_bucket": "**", "aws_user_files_s3_bucket_region": "eu-west-2" };

export default awsmobile;

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

No response

vincent38wargnier commented 1 year ago

The only solution I found for now is doing all my API calls like that :

try {
            response = await API.graphql({
                query: listRealisations,
                authMode: 'AWS_IAM'
            })
        }
        catch {
            response = await API.graphql({
                query: listRealisations,
                authMode: 'AMAZON_COGNITO_USER_POOLS',
            })
        }
chrisbonifacio commented 1 year ago

Hey, could you try adding another line to your auth rules like so:

{allow: private, operations: [read], provider: iam}

and let me know if this authorizes the request with IAM while logged in.

vincent38wargnier commented 1 year ago

Hi @chrisbonifacio, sorry for the delay I was on holidays. I tried what you proposed, and I'm still facing the same issue.

Here is now how looks like my table with its authorisations :

type Realisation @model
@auth(rules: [
  {allow: owner},
  {allow: public, operations: [read] , provider: apiKey},
  {allow: private, operations: [read]}
  {allow: public, operations: [read], provider: iam}
  {allow: private, operations: [read], provider: iam}
  ]){
  id: ID!
  name: String!
  description: String
  text: String
  position: Int! @index(name: "byPosition", queryField: "getRealisationByPosition")
  status: String! @index(name: "byStatus", queryField: "getRealisationByStatus")
  images: [ImgFile]
} 

I also tried that :

type Realisation @model
@auth(rules: [
  {allow: owner},
  {allow: public, operations: [read] , provider: apiKey},
  # {allow: private, operations: [read]}
  {allow: public, operations: [read], provider: iam}
  {allow: private, operations: [read], provider: iam}
  ]){
  id: ID!
  name: String!
  description: String
  text: String
  position: Int! @index(name: "byPosition", queryField: "getRealisationByPosition")
  status: String! @index(name: "byStatus", queryField: "getRealisationByStatus")
  images: [ImgFile]
} 

And here is the result :

image

I also tried in SSR, because hopefully I will do those requests in SSR and not dynamically and I have the same error :

image

And Once I disconnect my user from Cognito, everything works fine. It seems that some cookies of Congito might block any other auth modes?

Thank you

vincent38wargnier commented 1 year ago

I think I found the source of my problem : the user is not a basic user but is part of a Cognito group. I added {allow: groups, groups: ["admin"]}, but the access stays denied. Is there any solution to do a graphql Querry with AWS_IAM when unauthenticated users are allowed while being connected to Cognito as a group member?

I wanted to add this line to the authorisation : {allow: groups, groups: ["admin"], provider: iam} But I got the following error :

image

Here is how looks my table now :

type Realisation @model
@auth(rules: [
  {allow: owner},
  {allow: public, operations: [read] , provider: apiKey},
  {allow: groups, groups: ["admin"]}
  # {allow: private, operations: [read]}
  {allow: public, operations: [read], provider: iam}
  {allow: private, operations: [read], provider: iam}
  ]){
  id: ID!
  name: String!
  description: String
  text: String
  position: Int! @index(name: "byPosition", queryField: "getRealisationByPosition")
  status: String! @index(name: "byStatus", queryField: "getRealisationByStatus")
  images: [ImgFile]
}

I also noticed that if instead of AWS_IAM i'm using API_KEY, the request works, whether I'm connected or not to Congito. So why is it not the same for AWS_IAM?

alharris-at commented 1 year ago

Hi @vincent38wargnier, it sounds like your user pool may not have unauthenticated users enabled (which is disabled by default), so if a user is not signed in, they will not have access to the public/iam auth method.

You can enable this feature by updating your user pool via amplify update auth and going through the walkthrough of full features, setting 'allow unauthenticated users' to true.

enable-unauth-users

If that does not address your issue, feel free to reopen this request, and we'll continue working to assist you.