Skip to content

[typescript-axios] Add support for AWSv4 Signature #18215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

varqasim
Copy link

@varqasim varqasim commented Mar 23, 2024

Add AWS v4 signature support to typescript-axios client generator by making use of https://github.com/jamesmbourne/aws4-axios. Addresses request #7034.

Example

OpenAPI Definition

components:
    sigv4:
      type: apiKey
      name: Authorization
      in: header
      x-amazon-apigateway-authtype: awsSigv4
paths:
  /v1/user:
    post:
      operationId: createUser
      summary: Create a single user
      security:
        - sigv4: []
      ....

SDK Usage

import * as UserServiceSDK from 'sdk';

class ApiConfig extends UserServiceSDK.Configuration { }
const api = new UserServiceSDK.DefaultApi(new ApiConfig({
  basePath: process.env.SERVICE_A_BASE_URL,
  awsv4: {
    options: {
      region: process.env.AWS_REGION,
      service: 'execute-api'
    },
    credentials: {
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
      sessionToken: process.env.AWS_SESSION_TOKEN
    }
  }
}));

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package 
    ./bin/generate-samples.sh ./bin/configs/*.yaml
    ./bin/utils/export_docs_generators.sh
    
    (For Windows users, please run the script in Git BASH)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.1.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

@varqasim
Copy link
Author

varqasim commented Mar 23, 2024

@davidgamero @amakhrov Your reviews would be appreciated

@@ -71,6 +71,10 @@ export const {{classname}}AxiosParamCreator = function (configuration?: Configur
{{#authMethods}}
// authentication {{name}} required
{{#isApiKey}}
{{#withAWSV4Signature}}
// aws v4 signature authentication required
await setAWS4SignatureInterceptor(globalAxios, configuration)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this sets the interceptor to the shared axios instance - affecting all requests made with axios in the app

  • it's not limited to just endpoints configured for that in the spec
  • it adds multiple instances of the interceptor: every time a someOperationAxiosParamCreator function is called

I really think the security settings should be applied per endpoint rather than globally

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think the best way to achieve this? Would it be creating a "shared" axios instance just for the aws4 secured endpoints?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. It was my first thought, too. But I'm concerned it will create much more confusion down the line. Like, users might want to set some app-specific interceptors globally - but some endpoints won't be covered due to a standalone axios instance (please correct me if that's wrong - it's been some time since I actively worked with axios myself)

  2. Another option is not setting an interceptor per se (don't call axios.interceptor.request.use). An interceptor is essentially a function. You can call it directly inside your operation. This way you can do that granularly, conditionally - only when needed.

  3. A variation of the above is setting a custom global interceptor with a condition inside. Individual operations will add some optional flag to request config. The interceptor will analyze this flag and invoke the aws4-axios interceptor or not based on that.

I personally like option 2 best.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option 4:
If the primary use case is adding AWS4 to the whole spec (rather then to select individual operations in it), we don't even need to support it explicitly in the generator. A user can set a global inteceptor and call it a day :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option 4:
If the primary use case is adding AWS4 to the whole spec (rather then to select individual operations in it), we don't even need to support it explicitly in the generator. A user can set a global interceptor and call it a day :)

This is exactly my use case and to me it makes sense since an SDK that is built for inter-service communication only defines endpoints that are meant for such scenario and not any other. In my experience also an SDK uses a single type of authentication/authorization type and not multiple ones. Otherwise it's going to be a complex setup to handle. Shall we go with option 4?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, in this case perhaps you don't need any change in openapi-generator - you can set the global interceptor directly in your app

@@ -73,6 +75,7 @@ public TypeScriptAxiosClientCodegen() {
this.cliOptions.add(new CliOption(USE_SINGLE_REQUEST_PARAMETER, "Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
this.cliOptions.add(new CliOption(WITH_NODE_IMPORTS, "Setting this property to true adds imports for NodeJS", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
this.cliOptions.add(new CliOption(STRING_ENUMS, STRING_ENUMS_DESC).defaultValue(String.valueOf(this.stringEnums)));
this.cliOptions.add(new CliOption(WITH_AWSV4_SIGNATURE, "whether to include AWS v4 signature support", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if instead of requiring a user to explicitly opt in, we derive it from the actual spec? If the spec uses aws4 security, we add the corresponding dependency to the generated code.

Is there a use case where a spec relies on AWS4, but a user want to opt out? Or vice versa - the spec doesn't use AWS4, but we still want to include it in the generate files?

Copy link
Author

@varqasim varqasim Mar 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally don't have such use case since my workflow would be creating two different SDKs for each scenario. Scenario A would be an SDK that is exclusive for inter-service communication (IAM authorized) and scenario B would be creating a separate SDK for a frontend client that would have the a bearer token (i.e Cognito User Pool) authentication/authorisation security on it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

K I'm going to be taking this effort over.

  • @amakhrov I think your suggestion is great. I will attempt to include this in my new PR.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amakhrov Detecting AWS Auth from the schema PR #21356

@maratsh
Copy link

maratsh commented Aug 28, 2024

any chance to continue ?

@varqasim
Copy link
Author

varqasim commented Feb 9, 2025

@maratsh For now I opted in taking this branch which introduces aws4 and build it locally as a docker image and make use of it to achieve my goal. There are plans to continue this, but I am not sure if the direction I am taking this change is flexible enough for other use cases out there that want to achieve something similar.

@NiltiakSivad
Copy link

Hey @varqasim !! Thanks so much for this contribution. I was scoping out what it would take to implement this myself in typescript-axios and several other generators. I have not contributed to this repo before, so am working on learning how it all comes together. I am happy to help test with our use case as a consumer and collab on how to make this come together.

@varqasim
Copy link
Author

varqasim commented May 5, 2025

Hey @varqasim !! Thanks so much for this contribution. I was scoping out what it would take to implement this myself in typescript-axios and several other generators. I have not contributed to this repo before, so am working on learning how it all comes together. I am happy to help test with our use case as a consumer and collab on how to make this come together.

Hello @NiltiakSivad, I am glad you found this useful. Please go ahead and test it out if you need to or even take over the PR if you'd like. I am not sure how to take this further in order to get it approved as the discussion came to a halt, but what I did is built my own Docker image out of this PR and using it internally and works fine for us for the time being.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants