AWS Lambda can't send email to a verified SES email address - amazon-web-services

I have a static form-based HTML contact page, where the user needs to drop their Name, email address, subject and message and ultimately my script will reroute all the required value via AWS API --> Lambda --> my Gmail.
So, for that, I have a verified my GMAIL account in SES. And my AWS Lambda function is as below, (here, I used the same confirmed email address for to and send to deliver the email.)
'use strict';
console.log('Loading function');
const AWS = require('aws-sdk');
const sesClient = new AWS.SES();
const sesConfirmedAddress = "XXXX#gmail.com";
/**
* Lambda to process HTTP POST for a contact form with the following body
* {
"email": <contact-email>,
"subject": <contact-subject>,
"message": <contact-message>
}
*
*/
exports.handler = (event, context, callback) => {
console.log('Received event:', JSON.stringify(event, null, 2));
var emailObj = JSON.parse(event.body);
var params = getEmailMessage(emailObj);
var sendEmailPromise = sesClient.sendEmail(params).promise();
var response = {
statusCode: 200
};
sendEmailPromise.then(function(result) {
console.log(result);
callback(null, response);
}).catch(function(err) {
console.log(err);
response.statusCode = 500;
callback(null, response);
});
};
function getEmailMessage (emailObj) {
var emailRequestParams = {
Destination: {
ToAddresses: [ sesConfirmedAddress ]
},
Message: {
Body: {
Text: {
Data: emailObj.message
}
},
Subject: {
Data: emailObj.subject
}
},
Source: sesConfirmedAddress,
ReplyToAddresses: [ emailObj.email ]
};
return emailRequestParams;
}
Now in IAM, I have created a policy and attached to this lambda function. Then I have created API to use this function to serve my purpose.
Now when I click the TEST button in API, it gives me return code 200. which is great. But the problem is I don't see any email in my verified Gmail account.
I'm using, this as my test message,
{
"email": XXXX#example.com,
"subject": Test,
"message": This is a Test message
}
I tried also using postman and postman says everything is fine with 200 as return code. But still no email to my Gmail account. I checked in cloud watch logs everything is Green. So no idea why my verified Gmail is not receiving any kind of test message. Can anyome shed any lights here?
customized Role looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendTemplatedEmail",
"ses:SendRawEmail"
],
"Resource": "*"
}
]
}

I would you recommend two things, first try to deploy your code using serverless framework which can help you with the dependencies like IAM roles.
Second, As an alternative and complementary steps you may use the following reference from AWS:
Open the Amazon SES console at https://console.aws.amazon.com/ses/.
In the column on the left, choose either Email Addresses or Domains.
Select a verified email address or domain, and then choose Send a
Test Email. For To:, type bounce#simulator.amazonses.com. For
Subject and Body, type some sample text. Choose Send Test Email.
Repeat steps 3 and 4 to create another test message, but this time,
for To:, type complaint#simulator.amazonses.com.
Open the Amazon SQS
console at https://console.aws.amazon.com/sqs/. The Messages
Available column should indicate that 2 messages are available.

can you print the complete response to see if you get message id in the response ? if so, there seems to be no problem with the permission.
1. You can enable SNS notification for Bounce/Delivery/complaint for the verified identity in SES console and see if you receive any notification after making sendmail api call.
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notifications-via-sns.html
Additionally, also check recipient spam box.

Related

Adding SES as the destination in an AWS lambda function

After correctly creating a REST API trigger through API gateway and linking it with a lambda function, I am trying to add SES as the destination.
The purpose of the function would be to automatically forward messages received on a HTML contact form on a static website to an email address through SES service.
As per the caption I have created a lambda function I will show here below (just hid the domain and the email addresses for privacy).
var AWS = require('aws-sdk');
var ses = new AWS.SES();
var RECEIVER = 'XXX#gmail.com';
var SENDER = 'XXX#gmail.com';
var response = {
"isBase64Encoded": false,
"headers": { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': 'XXX.com'},
"statusCode": 200,
"body": "{\"result\": \"Success.\"}"
};
exports.handler = function (event, context) {
console.log('Received event:', event);
sendEmail(event, function (err, data) {
context.done(err, null);
});
};
function sendEmail (event, done) {
var params = {
Destination: {
ToAddresses: [
RECEIVER
]
},
Message: {
Body: {
Text: {
Data: 'name: ' + event.name + '\nphone: ' + event.phone + '\nemail: ' + event.email + '\ndesc: ' + event.desc,
Charset: 'UTF-8'
}
},
Subject: {
Data: 'Website Referral Form: ' + event.name,
Charset: 'UTF-8'
}
},
Source: SENDER
};
ses.sendEmail(params, done);
}
The relevant permissions to SES have been granted to the lambda function.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "ses:SendEmail",
"Resource": "*"
}
]
}
The testing of the function is indicated below:
Response
null
Function Logs
START RequestId: 765f6d27-9810-4d6c-a01b-a883eb429cd8 Version: $LATEST
2021-03-03T15:39:05.849Z 765f6d27-9810-4d6c-a01b-a883eb429cd8 INFO Received event: { key1: 'value1', key2: 'value2', key3: 'value3' }
END RequestId: 765f6d27-9810-4d6c-a01b-a883eb429cd8
REPORT RequestId: 765f6d27-9810-4d6c-a01b-a883eb429cd8 Duration: 928.32 ms Billed Duration: 929 ms Memory Size: 128 MB Max Memory Used: 87 MB Init Duration: 449.71 ms
Request ID
765f6d27-9810-4d6c-a01b-a883eb429cd8
Unfortunately, when I click on add destinations I cannot find the SES service to be selected.
The email address has already been validated in SES.
What else am I missing?
Thank you
SES isn't a supported destination. Lambda destinations are for sending the output of a Lambda function to another service, without writing code in the function for connecting directly to the service.
You don't need Lambda to send anything to an SES destination because you already sent the request directly in your code via ses.sendEmail().

AWS Cognito: how to allow users to change email without verification?

I'm new to AWS and I'm looking for a way to allow the users of my Android app to change their emails without going through the verification process (I managed to do it for the subscription).
I tried to follow this and this, and here is what I did.
In my Android app:
public void onClickChangeEmail(View view)
{
CognitoUserAttributes attributes = new CognitoUserAttributes();
attributes.getAttributes().put("email", "second#mail.com");
CognitoSettings
.getCognitoUserPool(MainActivity.this)
.getCurrentUser()
.updateAttributesInBackground(attributes, new UpdateAttributesHandler()
{
#Override
public void onSuccess(List<CognitoUserCodeDeliveryDetails> attributesVerificationList)
{
Log.i("tag", "Email updated!");
}
#Override
public void onFailure(Exception e)
{
e.printStackTrace();
}
});
}
Then, in my AWS console, I added a trigger in Cognito on Custom message, and here is my lambda function, which is triggered everytime a user updates his email:
const AWS = require('aws-sdk')
AWS.config.update({region: 'eu-central-1'});
exports.handler = (event, context, callback) => {
if (event.triggerSource === 'CustomMessage_UpdateUserAttribute')
{
const params = {
UserAttributes: [
{
Name: 'email_verified',
Value: 'true',
},
],
UserPoolId: event.userPoolId,
Username: event.userName,
};
var cognitoIdServiceProvider = new AWS.CognitoIdentityServiceProvider();
cognitoIdServiceProvider.adminUpdateUserAttributes(params, function(err, data) {
if (err) context.done(err, event); // an error occurred
else context.done(null, event); // successful response
});
}
else
{
context.done(null, event);
}
};
The result is: the email is properly updated (but it works whithout the lambda), but the lambda crashes, with the following error:
autoValidationUserEmailModification is not authorized to perform: cognito-idp:AdminUpdateUserAttributes
So it looks like an authorization is missing.
My questions are:
How can I fix the authorization part?
Is that method the right way to disable email verification on updating user email?
Thanks for your help.
Allow your function perform AdminUpdateUserAttributes on you Cognito Pool resource.
Update Lambda execution rules with block like:
{
"Action": [
"cognito-idp:AdminUpdateUserAttributes"
],
"Resource": "arn:aws:cognito-idp:eu-central-1:<your-user-id>:userpool/<your-user-pool>",
"Effect": "Allow"
}
where Resource is your Cognito User Pool ARN.

How to get/generate aws quicksight secure dashboard url

I want to embed Quicksight dashboard to an application. I have gone through the AWS quicksight documents, I did not get where I will find secure signed dashboard url.
In order to generate Quicksight secure dashboard url, follow the below steps:
Step 1: Create a new Identity Pool. Go to https://console.aws.amazon.com/cognito/home?region=us-east-1 , click ‘Create new Identity Pool’
Give an appropriate name.
Go to the Authentication Providers section, select Cognito.
Give the User Pool ID(your User pool ID) and App Client ID (go to App Clients in userpool and copy id).
Click ‘Create Pool’. Then click ‘Allow’ to create roles of the identity pool in IAM.
Step 2: Assign Custom policy to the Identity Pool Role
Create a custom policy with the below JSON.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "quicksight:RegisterUser",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "quicksight:GetDashboardEmbedUrl",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "sts:AssumeRole",
"Resource": "*",
"Effect": "Allow"
}
]
}
Note: if you want to restrict the user to only one dashboard, replace the * with the dashboard ARN name in quicksight:GetDashboardEmbedUrl,
then goto the roles in IAM.
select the IAM role of the Identity pool and assign custom policy to the role.
Step 3: Configuration for generating the temporary IAM(STS) user
Login to your application with the user credentials.
For creating temporary IAM user, we use Cognito credentials.
When user logs in, Cognito generates 3 token IDs - IDToken, AccessToken, RefreshToken. These tokens will be sent to your application server.
For creating a temporary IAM user, we use Cognito Access Token and credentials will look like below.
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId:"Identity pool ID",
Logins: {
'cognito-idp.us-east-1.amazonaws.com/UserPoolID': AccessToken
}
});
For generating temporary IAM credentials, we call sts.assume role method with the below parameters.
var params = {
RoleArn: "Cognito Identity role arn",
RoleSessionName: "Session name"
};
sts.assumeRole(params, function (err, data) {
if (err) console.log( err, err.stack); // an error occurred
else {
console.log(data);
})
You can add additional parameters like duration (in seconds) for the user.
Now, we will get the AccessKeyId, SecretAccessKey and Session Token of the temporary user.
Step 4: Register the User in Quicksight
With the help of same Cognito credentials used in the Step 3, we will register the user in quicksight by using the quicksight.registerUser method with the below parameters
var params = {
AwsAccountId: “account id”,
Email: 'email',
IdentityType: 'IAM' ,
Namespace: 'default',
UserRole: ADMIN | AUTHOR | READER | RESTRICTED_AUTHOR | RESTRICTED_READER,
IamArn: 'Cognito Identity role arn',
SessionName: 'session name given in the assume role creation',
};
quicksight.registerUser(params, function (err, data1) {
if (err) console.log("err register user”); // an error occurred
else {
// console.log("Register User1”);
}
})
Now the user will be registered in quicksight.
Step5: Update AWS configuration with New credentials.
Below code shows how to configure the AWS.config() with new credentials generated Step 3.
AWS.config.update({
accessKeyId: AccessToken,
secretAccessKey: SecretAccessKey ,
sessionToken: SessionToken,
"region": Region
});
Step6: Generate the EmbedURL for Dashboards:
By using the credentials generated in Step 3, we will call the quicksight.getDashboardEmbedUrl with the below parameters
var params = {
AwsAccountId: "account ID",
DashboardId: "dashboard Id",
IdentityType: "IAM",
ResetDisabled: true,
SessionLifetimeInMinutes: between 15 to 600 minutes,
UndoRedoDisabled: True | False
}
quicksight.getDashboardEmbedUrl(params,
function (err, data) {
if (!err) {
console.log(data);
} else {
console.log(err);
}
});
Now, we will get the embed url for the dashboard.
Call the QuickSightEmbedding.embedDashboard from front end with the help of the above generated url.
The result will be the dashboard embedded in your application with filter controls.
this link will give you what you need from aws cli https://aws.amazon.com/blogs/big-data/embed-interactive-dashboards-in-your-application-with-amazon-quicksight/
this is the step 3 aws cli cmd to give you embeded URL ( i was able to excecute)
aws quicksight get-dashboard-embed-url --aws-account-id (your account ID) --dashboard-id (your dashgboard ID) --identity-type IAM
there are many other dependence to enable the embeded dashboard per aws dcouments. i have not able to successfully doen that. GL and let me know if you make it happen!
PHP implementation
(in addition to Siva Sumanth's answer)
https://gist.github.com/evgalak/d0d1adf099e2d7bff741c16a89bf30ba

AWS quicksight login user programmatically without prompt

Our firm has built a custom admin portal and we want to provide our users a new link called reports which will link to aws quicksight dashboard but we don't want them to log in again in quicksight we cant the app (admin portal) to auto log them in with a specific role to see the dashboard. How can we achieve this?
Note: This answer is applicable only if you are using AWS Cognito
In order to generate Quicksight secure dashboard url, follow the below steps:
Step 1: Create a new Identity Pool. Go to https://console.aws.amazon.com/cognito/home?region=us-east-1 , click ‘Create new Identity Pool’
Give an appropriate name.
Go to the Authentication Providers section, select Cognito.
Give the User Pool ID(your User pool ID) and App Client ID (go to App Clients in userpool and copy id).
Click ‘Create Pool’. Then click ‘Allow’ to create roles of the identity pool in IAM.
Step 2: Assign Custom policy to the Identity Pool Role
Create a custom policy with the below JSON.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "quicksight:RegisterUser",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "quicksight:GetDashboardEmbedUrl",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "sts:AssumeRole",
"Resource": "*",
"Effect": "Allow"
}
]
}
Note: if you want to restrict the user to only one dashboard, replace the * with the dashboard ARN name in quicksight:GetDashboardEmbedUrl,
then goto the roles in IAM.
select the IAM role of the Identity pool and assign custom policy to the role.
Step 3: Configuration for generating the temporary IAM(STS) user
Login to your application with the user credentials.
For creating temporary IAM user, we use Cognito credentials.
When user logs in, Cognito generates 3 token IDs - IDToken, AccessToken, RefreshToken. These tokens will be sent to your application server.
For creating a temporary IAM user, we use Cognito Access Token and credentials will look like below.
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId:"Identity pool ID",
Logins: {
'cognito-idp.us-east-1.amazonaws.com/UserPoolID': AccessToken
}
});
For generating temporary IAM credentials, we call sts.assume role method with the below parameters.
var params = {
RoleArn: "Cognito Identity role arn",
RoleSessionName: "Session name"
};
sts.assumeRole(params, function (err, data) {
if (err) console.log( err, err.stack); // an error occurred
else {
console.log(data);
})
You can add additional parameters like duration (in seconds) for the user.
Now, we will get the AccessKeyId, SecretAccessKey and Session Token of the temporary user.
Step 4: Register the User in Quicksight
With the help of same Cognito credentials used in the Step 3, we will register the user in quicksight by using the quicksight.registerUser method with the below parameters
var params = {
AwsAccountId: “account id”,
Email: 'email',
IdentityType: 'IAM' ,
Namespace: 'default',
UserRole: ADMIN | AUTHOR | READER | RESTRICTED_AUTHOR | RESTRICTED_READER,
IamArn: 'Cognito Identity role arn',
SessionName: 'session name given in the assume role creation',
};
quicksight.registerUser(params, function (err, data1) {
if (err) console.log("err register user”); // an error occurred
else {
// console.log("Register User1”);
}
})
Now the user will be registered in quicksight.
Step5: Update AWS configuration with New credentials.
Below code shows how to configure the AWS.config() with new credentials generated Step 3.
AWS.config.update({
accessKeyId: AccessToken,
secretAccessKey: SecretAccessKey ,
sessionToken: SessionToken,
"region": Region
});
Step6: Generate the EmbedURL for Dashboards:
By using the credentials generated in Step 3, we will call the quicksight.getDashboardEmbedUrl with the below parameters
var params = {
AwsAccountId: "account ID",
DashboardId: "dashboard Id",
IdentityType: "IAM",
ResetDisabled: true,
SessionLifetimeInMinutes: between 15 to 600 minutes,
UndoRedoDisabled: True | False
}
quicksight.getDashboardEmbedUrl(params,
function (err, data) {
if (!err) {
console.log( data);
} else {
console.log(err);
}
}
);
Now, we will get the embed url for the dashboard.
Call the QuickSightEmbedding.embedDashboard from front end with the help of the above generated url.
The result will be the dashboard embedded in your application with filter controls.
AWS support single sign on.
I believe you can use that feature for the users.
please click here for more information

How to get the Cognito Identity id in AWS Lambda

How can I get the identity id of the user (logged in by AWS Cognito) that invoked an AWS Lambda function? Do I have to use the SDK on the Lambda function to get the identity id?
In AWS javascript SDK inside lambda function just use
context.identity.cognitoIdentityId
It is working for me
If anyone else stumbles upon this, I think this will help you a lot.
Note this only applies if you're using the Cognito User Pool Authorizer. If you want to use AWS_IAM with Cognito Identitys check out my github example https://github.com/VictorioBerra/js-cognito-auth-example (read down to EDIT area below)
If you have "Use Lambda Proxy Integration" checked then you wont have access to Request Template Mappings. But you can get to the claims inside the token in your lambda function:
exports.handler = (event, context, callback) => {
//create a response
const response = {
statusCode: 200,
body: JSON.stringify({
"user email": event.requestContext.authorizer.claims.email,
}),
};
callback(null, response);
};
EDIT - More information for using AWS_IAM as APIG authorizer
Basically you need to have your APIG secured with AWS_IAM AND you must auth via a Cognito Federated Identity which will return a sessionToken example using user pools. This is what makes the AWS IAM credentials temporary. Now you have everything you need to auth to your APIG.
To test this, download the desktop version of postman, toss in your API URI (grab this from the stages area) and then under Authorization fill out the 5 fields you need for Sig4 signing. You will see 'event.identity' object in your lambda function is loaded up with properties such as the user object.
If you want to use the APIG auto-generated SDK it comes built in with a factory that takes the accessKey, secret, and token and signs everything for you. Same with the aws-sdk. You can init the credentials with those three items and it will automatically sign all requests for you with those temp creds. If you want to straight up manually hit your API with window.fetch, request, curl, (insert http client here) you can calculate your own Sig4 (beware it can be a little complicated or use a modern library to do it for you.
Also for the record, while doing my research I noticed that if you want to NOT use AWS_IAM as an APIG authorizer, and you want to use "Cognito Identity Pool Authorizer" which is a fancy new option in the dropdown in APIG you can still get a ton of info on the user in the lambda event if you just pass the JWT gained from a successful Cognito popl auth to the APIG as the Authorization header. Inside that JWT is a lot of attributes which you can customize in your pool settings.
IMO professional opinion I think using the AWS_IAM temp creds authorizer is preferred. This way, you can use as many different IdPs as you want in Cognito Identities (Facebook, Twitter, pools, etc.)
If you go through API Gateway, you can pass the cognito id (as well as the user arn and other useful information) to Lambda. This solved the issue for me.
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
Per the docs, it looks like information about the identity provider would only be available for an invoke through the Mobile SDK.
To get around this, one option is to pass the identity ID to the function manually as part of the event. Assuming you are doing something like AWS.config.credentials = new AWS.CognitoIdentityCredentials(...) then you should be able to get the ID via AWS.config.credentials.identityId (after the credentials are refreshed).
EDIT: A better option for identity validation is to let Cognito/IAM handle it, and assume that if a user can successfully invoke a Lambda function, that means they are allowed to. In this case to manage per-user validation, take a look at whitelisting.
For anyone else still struggling to obtain the IdentityId of a Cognito User in a Lambda Function, after many hours of exploration, I discovered that the IdentityId can be obtained within a Lambda Function in the following ways depending on the method you use to invoke the Lambda Function:
Invocation via an API-Gateway trigger with a Cognito User Pool Authorizer
The IdentityId can be obtained in the following way:
const IDENTITY_POOL_ID = "us-west-2:7y812k8a-1w26-8dk4-84iw-2kdi849sku72"
const USER_POOL_ID = "cognito-idp.us-west-2.amazonaws.com/us-west-2_an976DxVk"
const { CognitoIdentityClient } = require("#aws-sdk/client-cognito-identity");
const { fromCognitoIdentityPool } = require("#aws-sdk/credential-provider-cognito-identity");
exports.handler = async (event,context) => {
const cognitoidentity = new CognitoIdentityClient({
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient(),
identityPoolId: IDENTITY_POOL_ID,
logins: {
[USER_POOL_ID]:event.headers.Authorization
}
}),
});
var credentials = await cognitoidentity.config.credentials()
console.log(credentials)
// {
// identityId: 'us-west-2:d393294b-ff23-43t6-d8s5-59876321457d',
// accessKeyId: 'ALALA2RZ7KTS7STD3VXLM',
// secretAccessKey: '/AldkSdt67saAddb6vddRIrs32adQCAo99XM6',
// sessionToken: 'IQoJb3JpZ2luX2VjEJj//////////...', // sessionToken cut for brevity
// expiration: 2022-07-17T08:58:10.000Z
// }
var identity_ID = credentials.identityId
console.log(identity_ID)
// us-west-2:d393294b-ff23-43t6-d8s5-59876321457d
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods" : "OPTIONS,POST,GET,PUT"
},
body:JSON.stringify(identity_ID)
};
return response;
}
Why?
Invoking a Lambda Function using an API-Gateway trigger with a Cognito User Pool Authorizer will result in the following fields (among additional fields which have been removed for brevity) being available in the event of the invocation:
{
"resource": "/{proxy+}",
"path": "/_",
"httpMethod": "POST",
"headers": {
"Authorization": "eyJrmlaod8(kyC9zdspo0jZIYjRENFlCNVBidnksl89DNm9USDJrT25MSDhjXC9EYkIzMzQ8389iYWxnIjoiUlMyNTYifQ.eyJzdWIiOiI8389yNDg4Ny0wYTRdkiuiOGQtODA4My1kNmIyYzZhYWNhN2YiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLXdlc3QtMi5hbWF6b25hd3MuY29tXC91cy13ZXN0LTJfZ3dPMjlMckR4IiwiY3VzdG9tOmZpcnN0X2tleSI6ImZhbHNlIiwiY29nbml0bzp1c2VybmFtZSI6IjJjNjI0ODg3LTBhNGItNGE4ZC04MDgzLWQ2YjJjNmFhY2E3ZiIsIm9yaWdpbl9qdGkiOiI0ODEzYzk1Ni01MDM0LTQzNmItYjE5OS00MWZjN2U1ZTQ0ODciLCJhdWQiOiJyNjdwOGdxNnQ5cHJrc2JyYmtxMDVoM2NuIiwiZXZlbnRfaWQiOiJmOWRlNWMxOC0yMTNlLTQ2NDgtOGY4MC1mODdhYWJhNWM4NGUiLCJjdXN0b206YXBpX2tleV9JRCI6Imhoa2hpMmw5cTkiLCJjdXN0b206YXBpX2tleSI6InVzLXdlc3QtMjplOTBkYmUzZC0wN2FhLTRjODQtOGQ0Ny04NjJmZDJhNzQ1MGZfcm5Zalk2YmhaT0lkWXhDcDBDcjNYZWVaYzFuT3ViZnkzdEs5aEtFRFVIZ3FJd1BSTVJuQ2pyWlJYdGNBSVpqWTVnMFFyejN3ek1keGoiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTY1ODA0ODg1OSwiZXhwIjoxNjU4MTM2MTk3LCJpYXQiOjE2NTgxMzI1OTcsImp0aSI6IjdmNTdiYWZlLTA1ZGUtNDgwZC1hMDgzLWY1MjlhN2YzNmI2YiIsImVtYWlsIjoidHJldmVuYXc3QGdtYWlsLmNvbSJ9.hk-8ajGP2jO0RQvzwZWp2d5T1BLiWL9q6vvrbXemLBbd2kb1kkBSvklfC_7WWvJoy1ukwNoq8Cx63U2hQfjJB077AHrHfN2PkJu4DG86vSdtSzrZVDQmle331UxopLQvzDZ1mejfmSFbo6x1ZeTbo39PRpox4pzsfeUAM1Rf8H6y8OrdPZa7Gh6gRkiN2IcwdBnXI4-Q6HX5QqiVzr2O4zEnNsqMFfsFA3aO05hnp7EYRWHgS6EgaQjfBirCoyerBkjFJTXynl76Jj4fK3-3KY4tw5EHplxkgAih7a9QTxy8SbRee8kh7fwrIGEO6CtgkM9v0XL7jBPtNtGTpqBRzw"
},
"requestContext": {
"authorizer": {
"claims": {
"sub": "231df9d7-0aab-4dsd-8389-d315d9daca7f",
"email_verified": "true",
"iss": "https://cognito-idp.us-west-2.amazonaws.com/us-west-2_an976DxVk",
"custom:first_key": "false",
"cognito:username": "231df9d7-0aab-4dsd-8389-d315d9daca7f",
"origin_jti": "8131rlpo-5123-436b-b199-41315d9d4487",
"aud": "r6315d9dt9sdls315d9d5h3cn",
"event_id": "f3dsp9d8-213e-4648-8f80-f8315d9dc84e",
"custom:name": "my_name",
"token_use": "id",
"auth_time": "1658148859",
"exp": "Mon Jul 18 09:23:17 UTC 2022",
"iat": "Mon Jul 18 08:23:17 UTC 2022",
"jti": "982sdafe-97rs-996l-i899-k315d9d09n7n",
"email": "jeff#amazon.com"
}
},
"extendedRequestId": "dSiT_dLdJdad9oP=",
"requestTime": "18/Jul/2022:08:23:27 +0000",
"requestTimeEpoch": 1658132607750,
"requestId": "387ss00s3-add0-62i9-l9s0-9a8dj8976dfa",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"sourceIp": "91.83.769.200",
"principalOrgId": null,
"accessKey": null,
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0",
"user": null
},
"domainName": "mskd8fpni2.execute-api.us-west-2.amazonaws.com",
"apiId": "mskds98ni2"
},
"body": "{\"name\":\"my_name\"}",
"isBase64Encoded": false
}
and the following fields being available in the context of the invocation:
{
"callbackWaitsForEmptyEventLoop": true,
"functionVersion": "$LATEST",
"functionName": "hello-world-jsHelloWorldFunction-kTdLswTRrkdS",
"memoryLimitInMB": "512",
"logGroupName": "/aws/lambda/hello-world-jsHelloWorldFunction-kTdLswTRrkdS",
"logStreamName": "2022/07/15/[$LATEST]95edi84719944bdi84a6cdi8451di849",
"invokedFunctionArn": "arn:aws:lambda:us-west-2:598763214571:function:hello-world-jsHelloWorldFunction-kTdLswTRrkdS",
"awsRequestId": "d8s57juq-3g39-49sk-8625-5849sk9be44I"
}
Notice the following things:
The Cognito User's IdentityId is not available in either the event or the context
Other information about the Cognito User is available in the event (email, sub, custom attributes, etc.)
The request invocation time is available in the event (the time that the lambda function was invoked which is available in event.requestContext.requestTime and event.requestContext.requestTimeEpoch)
Note: Please leave a comment if you are having a hard time setting up an API-Gateway trigger with a Cognito User Pool Authorizer for your Lambda Function, if people are interested I will add instructions on how to accomplish this.
Direct invocation using a Cognito User's credentials and the AWS SDK for JavaScript v3
The IdentityId can be obtained in the following way:
exports.handler = async (event,context) => {
var identity_ID = context.identity.cognitoIdentityId
console.log(identity_ID)
// us-west-2:d393294b-ff23-43t6-d8s5-59876321457d
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods" : "OPTIONS,POST,GET,PUT"
},
body:JSON.stringify(identity_ID)
};
return response;
}
Why?
Directly invoking a Lambda Function using the AWS SDK for Javascript v3 and a Cognito User's credentials will result in no additional information being available in the event of the invocation:
{
"name": "my_name"
}
however, the IdentityId is available in the context of the invocation:
{
"callbackWaitsForEmptyEventLoop": true,
"functionVersion": "$LATEST",
"functionName": "hello-world-jsHelloWorldFunction-kTdLswTRrkdS",
"memoryLimitInMB": "512",
"logGroupName": "/aws/lambda/hello-world-jsHelloWorldFunction-kTdLswTRrkdS",
"logStreamName": "2022/07/15/[$LATEST]95edi84719944bdi84a6cdi8451di849",
"identity": {
"cognitoIdentityId": "us-west-2:d393294b-ff23-43t6-d8s5-59876321457d",
"cognitoIdentityPoolId": "us-west-2:7y812k8a-1w26-8dk4-84iw-2kdi849sku72"
},
"invokedFunctionArn": "arn:aws:lambda:us-west-2:598763214571:function:hello-world-jsHelloWorldFunction-kTdLswTRrkdS",
"awsRequestId": "d8s57juq-3g39-49sk-8625-5849sk9be44I"
}
Notice the following things:
The Cognito User's IdentityId is available in context.identity.cognitoIdentityId
Additional information about the Cognito User is not available in the event or the context (email, sub, custom attributes, etc.)
The request invocation time is not available in either the event or context
Note: To directly invoke a Lambda Function using a Cognito User's credentials and the AWS SDK for JavaScript v3, you need to add a custom IAM Permission to the IAM Role assumed by authenticated Users in your Cognito User Pool. To do this:
Go to IAM -> Roles, and find the role for your user pool such as amplify-myapp-prod-12987-authRole
Click "Add permissions"
Click "Create inline policy"
Click "JSON"
Insert the following policy and replace the Function ARN with the ARN of your Lambda Function:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"lambda:InvokeFunctionUrl",
"lambda:InvokeFunction"
],
"Resource": [
"arn:aws:lambda:*:598763214571:function:hello-world-jsHelloWorldFunction-kTdLswTRrkdS"
]
}
]
}
Click "Review Policy"
Save the policy
Invocation methods that don't currently support authenticating users from a Cognito User Pool:
Invoking with caller credentials (this only works if you are using AWS_IAM authentication, not a Cognito User Pool Authorizer)
Invoking via a function URL (this only supports AWS_IAM authentication as well)
My observation is the following.
If you call the API Gateway with a signed Request where you actually provide the accesskey, secret and sessionToken which you can extract via (JS SDK):
AWS.config.credentials = new AWS.CognitoIdentityCredentials(...)
AWS.config.credentials.get(..)
And assumed that your lambda is called from API-Gateway via LAMBDA_PROXY and Authorizer AWS_IAM. You can only access user stuff in lambda with:
exports.create = function (event, context) {
secdata = event.requestContext.identity.cognitoAuthenticationProvider;
}
Then you will get, apart from other stuff, the "sub" of the cognito UserPool User. So if you really want to know more about the user, it seems you need to ask AWS again via SDK call.
For a Python Lambda, invoked via Javascript AWS SDK / Cognito / Amplify...
https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html
context.identity.cognito_identity_id
It should look something like this:
{aws region}:{ GUID }
Assuming you are using an Identity Pool, this will return the Cognito Federated Identity, that can be used for fine grained access control. This is safer than relying on the Javascript payload containing the identity id.
The Cognito Identity Pool Auth Role will need to have Lambda:InvokeFunction policy, otherwise the user won't be able to invoke the function in the first place.
Edit: This works when calling the Lambda function DIRECTLY, not via API Gateway.
Edit2: The Cognito user is allowed to call the lambda because it is explicitly set in the IAM Cognito Auth role.
I was using Kotlin and my Lambda handler was
override fun handleRequest(event: APIGatewayProxyRequestEvent, context: Context): APIGatewayProxyResponseEvent
But event.requestContext had no authorizer. The solution was to upgrade the dependency in build.gradle from
com.amazonaws:aws-lambda-java-events:2.1.0 to com.amazonaws:aws-lambda-java-events:2.2.7. After that, I got the username as follows.
val claims = requestContext.authorizer["claims"] as Map<String, String>
println(claims["cognito:username"])
println(claims["email"])