Skip to content

CORS error using Cognito identity in signed request to Lambda Function URL.

0

Greetings Kind Reader, I'm trying to control access to a lambda function url via a cognito identity pool backed by a cognito user pool. The lambda function creates a report summarizing a dynamoDB database and sends an email to the requestor with the report's contents. I have created an IAM policy, UsageInvokeURL00, which has the following statement:

{
    "Version": "2012-10-17",
    "Statement": [
        {   
            "Action": "lambda:InvokeFunctionUrl",
            "Resource": [
                "arn:aws:lambda:us-east-1:<accountid>:function:UsageReport00"
            ],  
            "Effect": "Allow",
            "Sid": "VisualEditor0"
        }   
    ]   
}

To test the Lambda function URL I've created an IAM user, UsageUser00, with the UsageInvokeURL00 policy attached. To test invocation of the function URL I've used curl and it works -- the emailed report arrives as expected:

curl --verbose --aws-sigv4 "aws:amz:us-east-1:lambda" -X GET --user "<UsageUser00-access-key>":"<UsageUser00-secret-key>" "https://<function-url>.lambda-url.us-east-1.on.aws/?start=20251101&end=20251114&granularity=day&grouping=computer

Ultimately, I'd like to have Cognito Identity Pool grant access to the function URL and a browser interface to make requesting a report easier. To this end, I've created a Cognito User Pool with me as a user and an identity pool with the user pool as identity provider. I can login to the identity pool via the browser. The authenticated role for the identity pool is service-role/usage-idp-role which has two policies attached, Cognito-authenticated-<random> and UsageInvokeURL00. My understanding is that the Cognito-authenticated-<random> policy allows me to get temporary credentials:

{
    "Version": "2012-10-17",
    "Statement": [
        {   
            "Effect": "Allow",
            "Action": [
                "cognito-identity:GetCredentialsForIdentity"
            ],  
            "Resource": [
                "*" 
            ]   
        }   
    ]   
}

Everything appears to work fine until I attempt to call the function URL via a signed fetch when I get a CORS error. I create the signed fetch using the credentials returned from GetCredentialsForIdentityCommand using the identity pool's returned identityId.

I've currently set the CORS options on the function URL to be wide open -- Allow origin: *; Expose headers: *, Allow headers: authorization; Allow methods: *, Max age: -, Allow credentials: true. But when it comes time to request the report by invoking the function URL I receive a CORS error on the pre-Flight request due to the lack of a Access-Control-Allow-Origin response header.

Exploring why this was happening I modified my curl request to an OPTIONS request and added an origin header -- I always receive the expected Access-Control-Allow-Origin with my origin as the allowed origin. I've replicated the browsers full set of request headers in my curl command line and it still includes my origin in the Access-Control-Allow-Origin response header.

But when I use the accessKey and secretKey returned in the browser from the call to GetCredentialsForIdentity as the curl --user command-line option, the OPTIONS request fails with a 403 forbidden response with a message, "The security token included in the request is invalid." If I take the accessKey and secretKey associated with the user UsageUser00 and paste them into the createSignedFetcher's options parameter before calling createSignedFetcher, the signedFetch call fails because there's no Access-Control-Allow-Origin header in the response.

I did some searching to try to understand whether I should be able to use temporary credentials on the command line with curl -- everything said I should be able to, but the examples were including the session token in an x-amz-security-token header and I wasn't. I couldn't get the temporary credentials to work using plain curl, so, I downloaded awscurl, a python script that prepares the options for you, and managed to invoke my lambda function from the command line with temporary credentials. That's good news! So, it appears to me that I'm not preparing the signed fetcher properly in my browser code.

Here's the code where i prepare the signed fetcher using the temporary credentials received: export async function getIdentityInfo(user) { try { id_token = user.id_token; const getIdInput = { AccountId: ${awsAccountId}, IdentityPoolId: ${cognitoIdentityPoolId}, Logins: {}, }; getIdInput.Logins[cognitoAuthority] = id_token; const getIdCmd = new GetIdCommand(getIdInput); let idResponse; try { idResponse = await cognitoClient.send(getIdCmd); } catch (error) { const { requestId, cfId, extendedRequestId } = error.$metadata; console.log({ requestId, cfId, extendedRequestId }); throw error; } identityId = idResponse.IdentityId;

    const getCredsInput = {
                  IdentityId: `${identityId}`,
                  Logins: {},
                  }; 
                  //CustomRoleArn: `${computerUsageInvokeRoleArn}`,
    getCredsInput.Logins[cognitoAuthority] = id_token;
    const getCredsCmd = new GetCredentialsForIdentityCommand(getCredsInput);
    const credsResponse = await cognitoClient.send(getCredsCmd);
    credentials = credsResponse.Credentials;
    //document.cookie = `credentials=${JSON.stringify(credentials)}; HttpOnly; Secure`;
              
    const options = { 
      service: "lambda",
      region: `${awsRegionId}`,
      credentials: {
        accessKeyId: credentials.AccessKeyId,
        secretAccessKey: credentials.SecretKey,
        sessionToken: credentials.SessionToken
      } 
    };
    signedFetch = createSignedFetcher(options);

  } catch (error) {
    logger.error(error);
  }
}

I'm going to search next to see if there's something wrong with the way I'm creating and calling the signed fetcher. I will post any resolution. If you have any suggestions I would appreciate you sharing them.

Thanks, Terrence

1 Answer
0

Based on your description, you're encountering a CORS issue when trying to invoke a Lambda Function URL using credentials from Cognito Identity Pool. This is a common challenge when working with authenticated requests across different origins.

There are a few key issues to address:

  1. Lambda Function URL CORS Configuration: While you've set CORS options to be wide open, there might be an issue with how the OPTIONS preflight request is being handled. Lambda Function URLs require specific CORS headers to be returned for preflight requests.

[+] https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html#urls-cors

  1. Authentication Token Issue: The error "The security token included in the request is invalid" when using temporary credentials from Cognito suggests there might be an issue with how the credentials are being used or formatted in the request.

  2. IAM Permissions: Your IAM policy looks correct for lambda:InvokeFunctionUrl, but you might also need lambda:InvokeFunction permission.

Here are some steps to resolve these issues:

For the authentication issue:

  • When using temporary credentials from Cognito Identity Pool, you need to include the session token along with the access key and secret key in your signed requests.
  • The AWS SigV4 signing process needs to include this session token as part of the Authorization header.

Implementation approach:

  1. Update your resource-based policy on the Lambda function to include both required permissions.
  2. When creating your signed fetch in the browser, ensure you're using all three components of the temporary credentials (access key, secret key, and session token).
  3. If you're implementing your own signing logic, make sure it properly handles the session token.
  4. Consider using AWS SDK v3's @aws-sdk/signature-v4-crt or similar libraries that handle the signing process correctly for temporary credentials.

If you're still experiencing issues, you might want to consider an alternative approach using API Gateway in front of your Lambda function, as it provides more robust CORS handling capabilities.
Sources
CORS Policy Issue When Calling Lambda Function from React (Amplify + Cognito Setup) | AWS re:Post
Tutorial: Creating a response streaming Lambda function with a function URL - AWS Lambda

answered a year ago

AWS
SUPPORT ENGINEER

revised a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.