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.36k stars 2.1k forks source link

Rest API: Providing an "x-api-key" is mutually exclusive with signing the request #13310

Open BearHanded opened 2 weeks ago

BearHanded commented 2 weeks ago

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

Authentication, REST API

Amplify Version

v6

Amplify Categories

auth, api

Backend

None

Environment information

``` # Put output below this line System: OS: macOS 14.4 CPU: (10) arm64 Apple M1 Pro Memory: 1.35 GB / 32.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 18.17.0 - ~/.nvm/versions/node/v18.17.0/bin/node Yarn: 1.22.17 - /opt/homebrew/bin/yarn npm: 9.6.7 - ~/.nvm/versions/node/v18.17.0/bin/npm Browsers: Chrome: 124.0.6367.91 Edge: 124.0.2478.67 Safari: 17.4 npmPackages: .... Removed in-house libs @types/flat: ^5.0.2 => 5.0.5 @types/node: ^18.17.0 => 18.19.31 (20.12.7) @types/yargs: ^15.0.10 => 15.0.19 @typescript-eslint/eslint-plugin: 5.18.0 => 5.18.0 (4.33.0) @typescript-eslint/parser: 5.18.0 => 5.18.0 (4.33.0) dotenv: ^16.4.5 => 16.4.5 (8.6.0, 10.0.0) eslint: 8.12.0 => 8.12.0 (7.32.0) eslint-config-airbnb: ^18.2.1 => 18.2.1 eslint-plugin-cypress: ^2.12.1 => 2.15.2 eslint-plugin-import: ^2.x => 2.29.1 eslint-plugin-jest: ^26.1.3 => 26.9.0 eslint-plugin-jsx-a11y: ^6.x => 6.8.0 eslint-plugin-react: ^7.24.0 => 7.34.1 eslint-plugin-react-hooks: ^2.x => 2.5.1 openwhisk-java: 1.0.0 openwhisk-ruby: 1.0.0 prettier: ^2.4.1 => 2.8.8 serverless: ^3.35.2 => 3.38.0 serverless-api-client-certificate: ^1.0.2 => 1.0.2 serverless-bundle: ^6.0.0 => 6.1.0 serverless-cloudfront-invalidate: ^1.12.2 => 1.12.2 serverless-dotenv-plugin: ^3.0.0 => 3.12.2 serverless-google-hello-world: 0.1.0 serverless-offline: ^12.0.4 => 12.0.4 serverless-offline-ssm: ^6.2.0 => 6.2.0 serverless-openwhisk-hello-world: 0.1.0 serverless-plugin-scripts: ^1.0.2 => 1.0.2 serverless-s3-sync: 2.0.0 => 2.0.0 serverless-stack-termination-protection: ^2.0.0 => 2.0.2 typescript: ^4.6.3 => 4.9.5 yargs: ^16.1.1 => 16.2.0 (15.4.1) npmGlobalPackages: corepack: 0.18.0 npm: 9.6.7 ```

Describe the bug

I'm currently migrating amplify versions to v6. I have a use case where my API gateway looks for the Authentication header provided by default and lambdas (REST API) assess other headers on the request. Providing a modified header with x-api-key like so now removes the default signed Authorization headers from the request:

await post({
  apiName: "my-api",
  path: "/somewhere",
  options: {
    headers: {"x-api-key": "fake-key"}
  }
}).response;

Looking through the amplify package to see where this is signed, I see in @aws-amplify/api-rest/dist/esm/apis/common/handler.mjs:

const iamAuthApplicable = ({ headers }, signingServiceInfo) => !headers.authorization && !headers['x-api-key'] && !!signingServiceInfo;

This appears to be a change from past versions. We were previously able to have an api key and the Authorization header passed through via amplify.

Some of this may be an anti-pattern of the system I am attempting to upgrade, however I don't see anything saying these two headers should be mutually exclusive. I'm attempting to upgrade the package without rearchitecting the application's auth.

Am I missing some configuration option or do I need to roll my own header signing pattern here? Alternatively is there a Signer object in the lib that isn't deprecated? I don't see one in the docs.

Expected behavior

Amplify provides an option to still sign a request given custom headers, or removes the mutual exclusiveness of the headers.

Reproduction steps

  1. Configure Amplify for Auth and API > REST

  2. Setup API Gateway with IAM Auth expecting the Authorization Header

  3. Lambdas as API handlers expect x-api-token

  4. API post with "x-api-key" is missing Authorization image

  5. API post without "x-api-key" header has Authorization image

Code Snippet

// Put your code below this line.
await post({
  apiName: "my-api",
  path: "/somewhere",
  options: {
    headers: {"x-api-key": "fake-key"}
  }
}).response;

// Vs 
await post({
  apiName: "my-api",
  path: "/somewhere",
  options: {
    // headers: {"x-api-key": "fake-key"}
  }
}).response;

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

No response

cwomack commented 2 weeks ago

@BearHanded, can you help us understand what you're looking for here and more context on why you'd want both api-key and Authorization header? This does appear to be a version-parity issue between v5 and v6, and we are digging into it on our side as well.

BearHanded commented 2 weeks ago

Our lambdas are using a JWT in the x-api-key field to parse user details and permissions out, whereas the default headers are just getting the user in the door with the API Gateway using aws_iam as an authorizer.

chrisbonifacio commented 1 week ago

Hi @BearHanded we've discussed this internally and will look into allowing headers such as x-api-key for signed requests

ashika112 commented 2 days ago

Hi, we are looking into this now and trying to work on fix without breaking different customer use cases. I will keep the ticket updated when we have a PR for this.