Search code examples
amazon-web-servicesgoaws-sdkaws-sdk-go

How to : Invoke API Gateway endpoint with IAM authorization using golang client


It seems pretty straight forward, but I am not able to figure out yet.

In the code such as below -

resp, err := http.Get("API Gateway endpoint url goes here")
if err != nil {
   log.Fatalln(err)
}

I am not sure how I tell http.get to use AWS auth. aws cli is configured the access key and secret on the host. So I do not want to again mention in the code.

Some posts mentioned about signing request. But I am not sure if that's the right way. In light of the AWS SDK in golang, this feels bit low level stuff. I would expect either

  1. There must a way in AWS SDK that encapsulates signing HTTP request etc. and does the job
  2. There must be a way to do this using http inbuild package by "somehow" attaching aws creds.

Any help is much, much appreciated!

Thanks Sandeep


Solution

  • Some posts mentioned signing requests. But I am not sure if that's the right way

    Yes, IAM Auth requires request signing (see docs here and here). The current method is Signature v4. Api Gateway accepts a request if it has the expected headers. "Signing" is the process of adding the right headers:

    # signature is derived from your secret key and the request contents.
    --header 'Authorization: AWS4-HMAC-SHA256 Credential=AKIASIAXTWO8D5GSN4CS/20220712/us-east-1/execute-api/aws4_request, SignedHeaders=host;x-amz-date, Signature=f2d8478ceff83d5cd0696502cb58a8331304846d11367d74608295c7acbfba0c'
    # date prevents third parties from intercepting your request and resubmitting it later
    --header 'X-Amz-Date: 20220712T124302Z'
    

    There must a way in AWS SDK that encapsulates signing HTTP request etc. and does the job

    For several languages (JS, Java, etc. but not Go), the get-sdk command can generate a SDK client for your Rest API that, among other conveniences, wraps the signature process: client.privateGet(params, body, additionalParams).

    There must be a way to do this using http inbuild package by "somehow" attaching aws creds

    Using the plain-old SDKs it's a bit more work to add the headers, easily wrappable in a resusable type:

    ctx := context.TODO()
    
    // define the request
    endpoint := "https://cbi3vltq21.execute-api.us-east-1.amazonaws.com"
    u, _ := url.ParseRequestURI(endpoint)
    u.Path = "prod/private"
    req, _ := http.NewRequest("GET", u.String(), nil)
    
    // get the credentials from the local config files
    cfg, _ := config.LoadDefaultConfig(ctx,
      config.WithRegion("us-east-1"),
      config.WithSharedConfigProfile("my-profile"))
    creds, _ :=  cfg.Credentials.Retrieve(ctx) 
    
    // hash the request body - hex value used in the signature
    hash := sha256.Sum256([]byte("")) // if the request has no body, use the empty string
    hexHash := fmt.Sprintf("%x", hash)
    
    // add the Authorization and X-Amz-Date headers to the request
    signer := v4.NewSigner()
    _ = signer.SignHTTP(ctx, creds, req, hexHash, "execute-api", cfg.Region, time.Now())
    
    // execute the request
    client := &http.Client{}
    resp, _ := client.Do(req)
    

    H/T to this SO answer.