Skip to content
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

How to use the typescript client for IAM based api gateway #1514

Open
purnasrivatsa96 opened this issue Jan 27, 2025 · 0 comments
Open

How to use the typescript client for IAM based api gateway #1514

purnasrivatsa96 opened this issue Jan 27, 2025 · 0 comments

Comments

@purnasrivatsa96
Copy link

purnasrivatsa96 commented Jan 27, 2025

I have a smithy model with lambda authorizer configuration -

@title("sample service")
@service(sdkId: "serviceName", arnNamespace: "execute-api")
@cors(origin: "*")
@integration(
    type: "aws_proxy"
    httpMethod: "POST"
    uri: "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunctionArn}/invocations"
    credentials: "${APIGatewayExecutionRoleArn}"
)
@httpApiKeyAuth(name: "x-session-token", in: "header")
@authorizers(
    PROTECTED_API_AUTH: {
        scheme: "smithy.api#httpApiKeyAuth"
        type: "request"
        uri: "authorizer-lambdaFunction-arn"
        resultTtlInSeconds: 0
    }
)
@authorizer("NONE")
@restJson1
@requestValidator("full")
service ServiceName {
    version: "2024-11-10"
    operations: [
        Create
        List
        Get
    ]
    errors: [
        smithy.framework#ValidationException
    ]
}


@http(method: "POST", uri: "/object")
@authorizer("PROTECTED_API_AUTH")
operation Create {
    input: CreateObjectRequest
    output: CreateObjectResponse
}

I have a typescript client generated via adding this in smithy-build.json -

"typescript-client-codegen": {
      "package": "@com.base/service-name-client",
      "packageVersion": "0.0.1"
    },

i use the openapi file generated by this smithy model in my cdk code for configuring api gateway.
Also i am editing the open api file slightly so i can configure a second api gateway as well that does not have lambda authorizer and is IAM based instead(keeping everything else same) -

    const api = new SpecRestApi(this, "ServiceApi", {
      restApiName: "ServiceApi",
      deployOptions: {
        stageName: props.stageName,
        loggingLevel: MethodLoggingLevel.INFO,
        dataTraceEnabled: true,
        metricsEnabled: true,
        accessLogDestination: new cdk.aws_apigateway.LogGroupLogDestination(
          accessLogs
        ), // Enable access logs
        accessLogFormat: cdk.aws_apigateway.AccessLogFormat.custom(
          `$context.extendedRequestId $context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] "$context.httpMethod $context.resourcePath $context.protocol" $context.status $context.responseLength $context.requestId`
        ),
      },
      apiDefinition: this.restApiDefinitionWithLambdaIntegration(
        resolve(__dirname, "../generated/openapi/ServiceName.openapi.json"),
        [["LambdaFunctionArn", lambdaFunction]],
        apiExecutionRole
      ),
    });

    const apiDef = this.restApiDefinitionWithLambdaIntegration(
      resolve(__dirname, "../generated/openapi/ServiceName.openapi.json"),
      [["LambdaFunctionArn", lambdaFunction]],
      apiExecutionRole
    );

    const apiDefinitionObject = (apiDef as any).definition;

    if (!apiDefinitionObject || typeof apiDefinitionObject !== "object") {
      throw new Error(
        "Invalid API definition object. Check if the 'definition' field is present."
      );
    }

    const apiDefSecurityFieldRemoved = JSON.parse(
      JSON.stringify(apiDefinitionObject, (key, value) =>
        key === "security" ? undefined : value
      )
    );

    const apiDefIamAuthorizer = this.createRestApiWithIamAuthorizer(
      apiDefSecurityFieldRemoved
    );


  createRestApiWithIamAuthorizer(apiDef: any): any {
    apiDef.security = [{ "aws.auth.sigv4": [] }];
    apiDef.components.securitySchemes = {
      "aws.auth.sigv4": {
        type: "apiKey",
        description: "AWS Signature Version 4 authentication",
        name: "Authorization",
        in: "header",
        "x-amazon-apigateway-authtype": "awsSigv4",
      },
    };
    return ApiDefinition.fromInline(apiDef);
  }

    const apiWithIamAuth = new SpecRestApi(this, "ServiceNameApiWithIam", {
      restApiName: "ServiceNameApiInternal",
      deployOptions: {
        stageName: props.stageName,
        loggingLevel: MethodLoggingLevel.INFO,
        dataTraceEnabled: true,
        metricsEnabled: true,
        accessLogDestination: new cdk.aws_apigateway.LogGroupLogDestination(
          accessLogsIamApig
        ), // Enable access logs
        accessLogFormat: cdk.aws_apigateway.AccessLogFormat.custom(
          `$context.extendedRequestId $context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] "$context.httpMethod $context.resourcePath $context.protocol" $context.status $context.responseLength $context.requestId`
        ),
      },
      apiDefinition: apiDefIamAuthorizer,
    });

    // Give the the rest api execute ARN permission to invoke the lambda.
    lambdaFunction.addPermission("ApiInvokeLambdaPermission", {
      principal: new iam.ServicePrincipal("apigateway.amazonaws.com"),
      action: "lambda:InvokeFunction",
      sourceArn: api.arnForExecuteApi(),
    });

    lambdaFunction.addPermission("IamAuthApiInvokeLambdaPermission", {
      principal: new iam.ServicePrincipal("apigateway.amazonaws.com"),
      action: "lambda:InvokeFunction",
      sourceArn: apiWithIamAuth.arnForExecuteApi(),
    });
  }

This give me 2 api gateways -

Image Image

I am using this the smithy generated client to make a request from another lambda function to the IAM based api gateway -

import { Service } from "@com.base/service-name-client";

// Configure the client
export const ServiceClient = new Service({
  endpoint: {
    protocol: "https",
    hostname: "IAMBasedApiGIDValue.execute-api.us-east-1.amazonaws.com",
    path: "/Prod",
  },
});


      await ServiceClient.create({
        Id: someValue,
      });

In the lambda logs from where this client invokes that IAM based APIG, I see -

2025-01-27T20:17:23.064Z	c26f0cf3-a99f-41b6-8e73-ff9510ef75c4	INFO	Received an unexpected error Error: HttpAuthScheme `smithy.api#httpApiKeyAuth` did not have an IdentityProvider configured.
    at /var/task/server/dist/node_modules/@com.base/service-name-client/node_modules/@smithy/core/dist-cjs/index.js:92:11
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async /var/task/server/dist/node_modules/@com.base/service-name-client/node_modules/@aws-sdk/middleware-logger/dist-cjs/index.js:34:22
    at async /var/task/server/dist/operations/Search.js:26:30
    at async handle (/var/task/server/dist/node_modules/@com.base/search-server/dist-cjs/server/SearchService.js:42:22)
    at async Runtime.handler (/var/task/server/dist/apigateway.js:14:34)

Why is it looking for httpApiKeyAuth ? What does this error mean ?
How do i fix this ?

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

No branches or pull requests

1 participant