hono icon indicating copy to clipboard operation
hono copied to clipboard

Support AWS SigV4 signatures for Lambdalith's running Hono with IAM Authorization - RPC Client-side

Open NicoPowers opened this issue 1 year ago • 2 comments

What is the feature you are proposing?

Hello,

It would be awesome if we can somehow support SigV4 requests for lambdaliths running Hono, that way we don't have to use API Gateway (and incur the additional costs of that) and we can still use IAM Auth for AWS Lambda in URL mode.

I can create a signature like so:

const lambdaSignature = new SignatureV4({
            credentials: {
              accessKeyId: response.Credentials.AccessKeyId,
              secretAccessKey: response.Credentials.SecretKey,  
              sessionToken: response.Credentials.SessionToken                          
            },
            region: "us-east-1",
            service: "lambda",
            sha256: Sha256,
          })

Using the @smithy/signature-v4 package that is supported by AWS SDK v3, but then I would need to sign this signature like so:

const req = new HttpRequest({
    hostname: endpoint,
    path,
    method: "POST",
    body: JSON.stringify(data),
    headers: {
      host: endpoint,
      "Content-Type": "application/json",
    },
  });
  const signed = await lambdaSignature .sign(req, { signingDate: new Date() });

As you can see, this would require me to know beforehand all the requests that Hono will generate when doing RPC like functionality with the server/client.

It would be nice if we can have a middleware where it can use the Signature class to sign each request we make out of Hono for us automatically.

For reference, this is how I am doing this on the client side using AWS Cognito and the Hosted UI provided by Cognito:

import { SignatureV4 } from "@smithy/signature-v4"
import {Sha256} from '@aws-crypto/sha256-js';
import Cookies from "js-cookie";
import {
  CognitoIdentityClient,
  GetCredentialsForIdentityCommand,
  GetCredentialsForIdentityCommandInput,
  GetIdCommand,
  GetIdCommandInput,
} from "@aws-sdk/client-cognito-identity";

...

// check local document cookie for id token to see if user has authenticated with Cognito
      // if not, redirect to login page
      if (Cookies.get("id_token") === undefined) {
        throw redirect(
          `${import.meta.env.VITE_LOGIN_URL_NO_REDIRECT}&redirect_uri=${
            window.location.protocol
          }//${window.location.host}/post-login`
        );
      }
      // if we have an id token, we need to generate IAM credentials for this authenticated user via GetCredentialsForIdentityCommand
      // first get identity id (not the same thing as identity pool id!)
      const client = new CognitoIdentityClient({ region: "us-east-1" });

      const getIdInput: GetIdCommandInput = {
        IdentityPoolId: import.meta.env.VITE_IDENTITY_ID,
        Logins: {
          [`cognito-idp.us-east-1.amazonaws.com/${
            import.meta.env.VITE_USER_POOL_ID
          }`]: Cookies.get("id_token")!,
        },
      };

      const getIdCommand = new GetIdCommand(getIdInput);
      const getIdResponse = await client.send(getIdCommand);

      // now that we have the identity id, we can get the credentials
      if (getIdResponse.IdentityId) {
        const getCredentialsInput: GetCredentialsForIdentityCommandInput = {
          IdentityId: getIdResponse.IdentityId,
          Logins: {
            [`cognito-idp.us-east-1.amazonaws.com/${
              import.meta.env.VITE_USER_POOL_ID
            }`]: Cookies.get("id_token")!,
          },
        };
        const command = new GetCredentialsForIdentityCommand(
          getCredentialsInput
        );
        const response = await client.send(command);

        // now we need to create the signature for the API request that we will send in the headers
        if (response.Credentials && response.Credentials.AccessKeyId && response.Credentials.SecretKey) {
          const lambdaSignature = new SignatureV4({
            credentials: {
              accessKeyId: response.Credentials.AccessKeyId,
              secretAccessKey: response.Credentials.SecretKey,  
              sessionToken: response.Credentials.SessionToken                          
            },
            region: "us-east-1",
            service: "lambda",
            sha256: Sha256,
          })          

          // now we need to sign the request with the Signature
          // STUCK HERE, WE WOULD NEED TO KNOW THE REQUEST BEFORE WE SIGN IT
          const signedRequest = lambdaSignature.sign(stringToSign);
        
        }

NicoPowers avatar Sep 04 '24 16:09 NicoPowers

Please take a look at the following PR I'm working on for CDK! https://github.com/aws/aws-cdk/pull/31339. This enables CloudFront + OAC + Function URL integration. If this can be implemented smoothly, wouldn't it make some things unnecessary?

watany-dev avatar Sep 07 '24 11:09 watany-dev

Please take a look at the following PR I'm working on for CDK! aws/aws-cdk#31339. This enables CloudFront + OAC + Function URL integration. If this can be implemented smoothly, wouldn't it make some things unnecessary?

Why? Thats CDK only

ryanleecode avatar Oct 12 '24 22:10 ryanleecode