Reputation: 1435
Having a look at the AWS documentation,
you have the following paramaters available in the Pre Sign-up Lambda fuction:
"request": {
"userAttributes": {
"string": "string",
....
},
"validationData": {<validation data as key-value (String, String) pairs, from the client>}
is there a way to modify or add additional userAttributes the the event object?
for example:
// Modify an existing username...
event.request.userAttributes.name.ucfirst();
// Add an additional attribute...
event.request.userAttributes.nickname = "ANY_NAME";
callback(null, event);
Upvotes: 44
Views: 39777
Reputation: 3440
My use case was to add extra information into the signInUserSession:idToken so I didnt have to do an extra query in my backend to get this specific piece of data but just get it from the IDToken.
After reviewing the answers, I found a solution in the AWS docs that works:
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html#aws-lambda-triggers-pre-token-generation-example-1
So this Pretoken generation event works for me:
exports.handler = async (event) => {
console.log('event', event)
event.response = {
claimsOverrideDetails: {
claimsToAddOrOverride: {
my_first_attribute: "first_value",
my_second_attribute: "second_value",
'custom:newEmail': 'foo'
},
claimsToSuppress: ["email"],
},
};
return event;
};
The custom:newEmail
is an existing custom attribute which was changed after logging in with Cognito, but the my_first_attribute attribute was not registered in Cognito and also showed up in my idToken.
So that is great!
Upvotes: 0
Reputation: 4635
Yes, there's absolutely a way! You need to use AWS javascript SDK in your Lambda handler:
const AWS = require('aws-sdk');
AWS.config.update({region: 'ap-southeast-1'});
const cognitoidentityserviceprovider =
new AWS.CognitoIdentityServiceProvider({
apiVersion: '2016-04-18'
});
cognitoidentityserviceprovider.adminUpdateUserAttributes(
{
UserAttributes: [
{
Name: 'YOUR_USER_ATTRIBUTE_NAME',
Value: 'YOUR_USER_ATTRIBUTE_VALUE'
}
],
UserPoolId: event.userPoolId,
Username: event.userName
},
function(err, data) {
...
}
);
Make sure to give your Lambda function the right policies (i.e. allows "cognito-idp:AdminUpdateUserAttributes" action) and the user pool has the attribute defined.
Upvotes: 21
Reputation: 411
Yes ofcourse. You need to use AWS SDK.
const AWS = require('aws-sdk');
const config = require('./config');
function updateAttribute(params) {
AWS.config.update({
'region' : config.AWSConfig.region,
'accessKeyId': config.AWSConfig.accessKeyId,
'secretAccessKey': config.AWSConfig.secretAccessKey
});
let cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider();
let parameters = { UserPoolId : config.userPoolDetails.userPoolId,
Username : params.userName,
UserAttributes : [
{
'Name': params.nameOfAttribute ,
'Value': params.newValueOfAttribute
},
]}
cognitoIdentityServiceProvider.adminUpdateUserAttributes(parameters,function (err, result) {
if(err)
console.log(err);
else
console.log("Attribute updated successfully");
})
}
let params = {
userName : 'username',
nameOfAttribute : 'name',
newValueOfAttribute : 'Sachin'
}
updateAttribute(params);
You can even add new attribute like this.
You can read more here : https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminUpdateUserAttributes.html
Upvotes: 1
Reputation: 880
To fill in some detail around @Khoi's very helpful answer, and for all you cut-and-pasters (you know who you are), here is a template for a Lambda that runs off a Cognito User Pool Post Confirmation trigger.
The Lambda sets a new value in a user's custom attribute, which is "fruit" in this example. Here are some gotchas to avoid in the implementation minefield:
let a = user.attributes['custom:fruit']
const aws = require('aws-sdk');
const cisProvider = new aws.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
// Cognito User Pool Lambda triggers are documented here:
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html
exports.lambdaHandler = async (event, context, callback) => {
const params = {
UserPoolId: event.userPoolId,
Username: event.userName,
UserAttributes: // this parameter needs to be an array
[
{
Name: 'custom:fruit',
Value: 'banana'
}
]
};
if (event.request.userAttributes.email) {
try {
await cisProvider
.adminUpdateUserAttributes(params)
.promise();
console.log('Success');
} catch (error) {
console.error('Error', error);
}
}
callback(null, event);
};
If you are using AWS SAM (as I hope you are) to debug and deploy your Lambda, here is a template for this Lambda. Note the Role resource. Before deploying the Lambda you will need to define this IAM role in the AWS console. There probably is a way to define the role right here in the template, but I'm not that expert with AWS YAML. Deploy the Lambda to your AWS account with the SAM CLI command
sam deploy --guided
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
SAM Template for lambda function that runs as a Cognito User Pool post confirmation trigger.
Cognito invokes this function when a new user signs up.
Globals:
Function:
Timeout: 3
Resources:
PostConfirmationFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: post-confirmation/
Handler: app.lambdaHandler
Runtime: nodejs14.x
Role:
# This role gives Lambda permission to update user pool attributes as well as basic execution.
arn:aws:iam::xxxxxxxxxxxx:role/lambda-cognito-update-role
Outputs:
PostConfirmationFunction:
Description: "Post Confirmation Lambda Function ARN"
Value: !GetAtt PostConfirmationFunction.Arn
You need to create a role in the IAM console that includes permission to update user attributes. I prefer to do this with an "inline policy" to avoid a proliferation of IAM policies in my account with unclear dependency. The best way I have found to do this is a two step process:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CognitoUpdate",
"Effect": "Allow",
"Action": "cognito-idp:AdminUpdateUserAttributes",
"Resource": "*"
}
]
}
Arguably the resource should be more specific, limited only to user pools in your account maybe. I leave that to your discretion.
Upvotes: 5
Reputation: 5150
For anyone else looking into insight on this question, here is an example below
The lambda function #1 below takes into two custom attributes ida
and ethaddress
. The lambda is invoked during the PreSignUpHook for Cognito user pools
#2 (Before event changed logs) the original values for these attributes is ida=1
and ethaddress=ABCD
#3 (After event changed logs) reflects the changed values of these attributes:
ida=2
and ethaddress=EFGH
However the values which are saved to cognito are the original ones: ida=1
and ethaddress=ABCD
. Therefore updateing the userAttributes during the presignuphook does NOT work as suggested in some of the answers.
On a side note when the predefined attributes in the response object are modified they are updated as expected:
"response": {
"autoConfirmUser": true,
"autoVerifyEmail": false,
"autoVerifyPhone": false
}
1. LAMBDA:
'use strict';
global.fetch = require('node-fetch')
module.exports.preSignUp = async (event, context, callback) => {
// Set the user pool autoConfirmUser flag after validating the email domain
let data = await fetch("http://***.***.***/api/members/create",
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
})
.then(res => res.json())
.then(res => res);
event.response.autoConfirmUser = true;
console.log('before event:', JSON.stringify(event));
event.request.userAttributes['custom:ethaddress'] = String(data.address);
event.request.userAttributes['custom:ida'] = "2";
console.log('Received event:', JSON.stringify(event));
console.log('Address:', data.address);
// Return to Amazon Cognito
callback(null, event);
};
2.
BEFORE EVENT CHANGE LOG:
2019-01-20T01:02:24.639Z edce636e-75ea-492b-b6a0-dd4f22dc9038 before event:
{
"version": "1",
"region": "us-east-1",
"userPoolId": "us-east-1-*****",
"userName": "*******@gmail.com",
"callerContext": {
"awsSdkVersion": "aws-sdk-unknown-unknown",
"clientId": "******************"
},
"triggerSource": "PreSignUp_SignUp",
"request": {
"userAttributes": {
"custom:ida": "1",
"custom:ethaddress": "ABCD",
"email": "*******@gmail.com"
},
"validationData": {}
},
"response": {
"autoConfirmUser": true,
"autoVerifyEmail": false,
"autoVerifyPhone": false
}
}
3 .
AFTER EVENT CHANGE LOG:
Received event:
{
"version": "1",
"region": "us-east-1",
"userPoolId": "us-east-1_0BaE6eaTY",
"userName": "*******@gmail.com",
"callerContext": {
"awsSdkVersion": "aws-sdk-unknown-unknown",
"clientId": "*****************"
},
"triggerSource": "PreSignUp_SignUp",
"request": {
"userAttributes": {
"custom:ida": "2",
"custom:ethaddress": "EFGH",
"email": "*******@gmail.com"
},
"validationData": {}
},
"response": {
"autoConfirmUser": true,
"autoVerifyEmail": false,
"autoVerifyPhone": false
}
}
UPDATE:
It seems like there is no way to do this as a part of the PRESIGNUP process However it is possible to do this as a POSTCONFIRMATION trigger in cognito example provided below.
Some things to watch out for.
module.exports.postConfirmation = async (event, context,callback) => {
const cognitoIdServiceProvider = new CognitoIdentityServiceProvider({
region: 'us-east-1'
});
var params = {
UserAttributes: [
{
Name: 'custom:sillyName',
Value: 'customSillyName'
}
],
UserPoolId: event.userPoolId,
Username: event.userName
}
cognitoIdServiceProvider.adminUpdateUserAttributes(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
callback(null,event);
};
Note if you attempt to user cognitoIdServiceProvider.adminUpdateUserAttributes
in the preSignUp trigger hook you;ll get an exception saying the user does not exit yet
Upvotes: 7
Reputation: 1584
There isn't a way to mutate/augment attributes during sign up, but during sign in, you can mutate/augment them with the pre-token generation trigger.
Upvotes: 10