Amazon Cognito invokes this trigger after a new user is confirmed, allowing you to send custom messages or to add custom logic. For example, you could use this trigger to gather new user data.,The post confirmation Lambda function is triggered just after Amazon Cognito confirms a new user. See these user confirmation tutorials for JavaScript, Android, and iOS.,These are the parameters that Amazon Cognito passes to this Lambda function along with the event information in the common parameters.,Amazon Cognito passes event information to your Lambda function. The function then returns the same event object to Amazon Cognito, with any changes in the response. In the Lambda console, you can set up a test event with data that is relevant to your Lambda trigger. The following is a test event for this code sample:
{
"request": {
"userAttributes": {
"string": "string",
...
},
"clientMetadata": {
"string": "string",
...
}
},
"response": {}
}
var aws = require('aws-sdk');
var ses = new aws.SES();
exports.handler = (event, context, callback) => {
console.log(event);
if (event.request.userAttributes.email) {
sendEmail(event.request.userAttributes.email, "Congratulations " + event.userName + ", you have been confirmed: ", function(status) {
// Return to Amazon Cognito
callback(null, event);
});
} else {
// Nothing to do, the user's email ID is unknown
callback(null, event);
}
};
function sendEmail(to, body, completedCallback) {
var eParams = {
Destination: {
ToAddresses: [to]
},
Message: {
Body: {
Text: {
Data: body
}
},
Subject: {
Data: "Cognito Identity Provider registration completed"
}
},
// Replace source_email with your SES validated email address
Source: "<source_email>"
};
var email = ses.sendEmail(eParams, function(err, data){
if (err) {
console.log(err);
} else {
console.log("===EMAIL SENT===");
}
completedCallback('Email sent');
});
console.log("EMAIL CODE END");
};
{
"request": {
"userAttributes": {
"email": "user@example.com",
"email_verified": true
}
},
"response": {}
}
You create custom workflows by assigning Lambda functions to user pool triggers. When you use the AdminRespondToAuthChallenge API action, Amazon Cognito invokes any functions that you have assigned to the following triggers:,You create custom workflows by assigning Lambda functions to user pool triggers. When you use the AdminInitiateAuth API action, Amazon Cognito invokes the Lambda functions that are specified for various triggers. The ClientMetadata value is passed as input to the functions for only the following triggers:,You create custom workflows by assigning Lambda functions to user pool triggers. When you use the InitiateAuth API action, Amazon Cognito invokes the Lambda functions that are specified for various triggers. The ClientMetadata value is passed as input to the functions for only the following triggers:,When you use the AdminInitiateAuth API action, Amazon Cognito also invokes the functions for the following triggers, but it doesn't provide the ClientMetadata value as input:
import boto3
client = boto3.client('cognito-idp')
response = client.add_custom_attributes(
UserPoolId = 'string',
CustomAttributes = [{
'Name': 'string',
'AttributeDataType': 'String' | 'Number' | 'DateTime' | 'Boolean',
'DeveloperOnlyAttribute': True | False,
'Mutable': True | False,
'Required': True | False,
'NumberAttributeConstraints': {
'MinValue': 'string',
'MaxValue': 'string'
},
'StringAttributeConstraints': {
'MinLength': 'string',
'MaxLength': 'string'
}
}, ]
)
{}
response = client.admin_add_user_to_group(
UserPoolId = 'string',
Username = 'string',
GroupName = 'string'
)
response = client.admin_confirm_sign_up(
UserPoolId = 'string',
Username = 'string',
ClientMetadata = {
'string': 'string'
}
)
response = client.admin_create_user(
UserPoolId = 'string',
Username = 'string',
UserAttributes = [{
'Name': 'string',
'Value': 'string'
}, ],
ValidationData = [{
'Name': 'string',
'Value': 'string'
}, ],
TemporaryPassword = 'string',
ForceAliasCreation = True | False,
MessageAction = 'RESEND' | 'SUPPRESS',
DesiredDeliveryMediums = [
'SMS' | 'EMAIL',
],
ClientMetadata = {
'string': 'string'
}
)
(As a workaround, I would be happy with a post confirmation lambda trigger that creates for example a ${cognito-identity.amazonaws.com:sub}/info.txt file in some S3 bucket, and in the info.txt file it could place the user sub from the user pool. I am not sure that this is feasible at all, it was just an idea.),I tried to get that the identityId of that user via a post confirmation lambda trigger to store in DynamoDB, but no chance.,Theoretically, /protected/${cognito-identity.amazonaws.com:cognito:username}/* should work without changing IAM because it’s a built-in claim but it doesn’t … no idea why. Suggested solutions didn’t work for me, getting 403 on PutObject,@richardzcode This map must exist on the backend, because a given user always gets the same ${cognito-identity.amazonaws.com:sub}. It must be solvable purely on the backend.
After doing some more research we found that you can use user attributes for access control so instead of using the federated id as the users folder name, you can specify a custom attribute mapping (we mapped cognitoId to sub) using principal tags, and in your policy you can dynamically reference resources using these tags:
{
"Sid": "ReadWriteDeleteYourObjects",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::bucket-name/private/${aws:PrincipalTag/cognitoId}",
"arn:aws:s3:::bucket-name/private/${aws:PrincipalTag/cognitoId}/*"
]
}
Ok, this works for me:
async signIn({
commit
}, {
username,
password
}) {
const user = await Auth.signIn(username, password);
const credentials = await Auth.currentCredentials();
console.log('Cognito identity ID:', credentials.identityId);
authenticate(commit, user);
},
From
"Action": "sts:AssumeRoleWithWebIdentity",
to
"Action": [
"sts:AssumeRoleWithWebIdentity",
"sts:TagSession"
],
In it, global replace
$ { cognito - identity.amazonaws.com: sub }
with
$ { aws: PrincipalTag / cognitoId }
In the client code, you will need to specify the identityId
field by supplying sub
to it - this is how you use storage in client code:
this.amplifyService.storage().put(
s3key, // s3 key
blob, // data to put in s3
{
level: 'protected',
identityId: sub // user's sub value is provided here, not the identityId
}
)
AppSync Lambda-request:
{
"version": "2017-02-28",
"operation": "Invoke",
"payload": $util.toJson({
"sub": $ctx.identity.sub,
"jwt": $ctx.request.headers.authorization
})
}
Lambda:
const AWS = require('aws-sdk')
const cip = new AWS.CognitoIdentity()
const ddb = new AWS.DynamoDB.DocumentClient()
exports.handler = async ({
sub,
jwt
}, context, callback) => {
let {
IdentityId: id
} = await cip.getId({
IdentityPoolId: process.env.IDENTITY_POOL_ID,
Logins: {
[process.env.USER_POOL_ID]: jwt
}
}).promise()
await ddb.update({
TableName: 'Users',
Key: {
id: sub
},
UpdateExpression: 'set identityToken = :id',
ExpressionAttributeValues: {
':id': id
}
}).promise()
callback(null, {})
}
Last updated: Apr 22, 2021
Copied!import {Callback, Context, PostConfirmationTriggerEvent} from 'aws-lambda';
import AWS from 'aws-sdk';
export async function main(
event: PostConfirmationTriggerEvent,
_context: Context,
callback: Callback,
): Promise<void> {
const {userPoolId, userName} = event;
try {
await adminAddUserToGroup({
userPoolId,
username: userName,
groupName: 'Users',
});
return callback(null, event);
} catch (error) {
return callback(error, event);
}
}
export function adminAddUserToGroup({
userPoolId,
username,
groupName,
}: {
userPoolId: string;
username: string;
groupName: string;
}): Promise<{
$response: AWS.Response<Record<string, string>, AWS.AWSError>;
}> {
const params = {
GroupName: groupName,
UserPoolId: userPoolId,
Username: username,
};
const cognitoIdp = new AWS.CognitoIdentityServiceProvider();
return cognitoIdp.adminAddUserToGroup(params).promise();
}