Using AWS Lambda to delete a Cognito User - node.js

I want to give user's the ability to delete their account in my android app. I already set up a login/sig up functionality with AWS Amplify and a AWS Cognito User Pool. But Amplify doesn't provide a "delete User" functionality, so I wanted to use a lambda function to delete a user from my cognito user pool.
The function will be called when the user clicks on "delete my account" in the app. To test the function, I use a hard coded username in the Lambda function, instead of passing one into the function. But even that doesn't work. After deploying the Lambda function, I run the function by clicking on "Test" in the console. The console then shows Execution result: succeeded but the response is null. I would either epect a Status 200 or 400 as response. And in the CloudWatch logs of the Execution I can only see my first log statement ("I was here"), the other two don't show up. And in the Cognito Console the user is still there.
This is my Lambda Code (Node.js):
const AWS = require('aws-sdk');
console.log("I was here");
var params = {
UserPoolId: 'syz****f-dev',
Username: '5b53****138'
};
var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({
"region": 'eu-central-1',
});
exports.handler = async (event) => {
cognitoidentityserviceprovider.adminDeleteUser(params, function(err, data) {
if (err) {
var response = {
statusCode: 400,
body: JSON.stringify('Didnt work!'),
};
console.log(err, err.stack);
return response;
}
else {
response = {
statusCode: 200,
body: JSON.stringify('yeah!'),
};
console.log(data);
return response;
}
});
};
The user "5b53....138" is still there in my Cognito User Pool "syz....f-dev" after I test this function:
This is the log file that I found in Cloudwatch:
My Lambda Function has a role with these 3 policies and I used the IAM Policy Simulator and the action AdminDeleteUser is allowed with AmazonCognitoAuthenticatedIdentities, so this shouldn`t be the problem:
AmazonCognitoAuthenticatedIdentities
AmazonCognitoPowerUser
AWSLambdaBasicExecutionRole
In CloudWatch I can see that the function got invoked.

First of all, your user pool id is wrong, find the correct on by opening your Cognito user pool: The first thing you see when opening your user pool in the console is the id:
It starts with your region followed by a _, in your case eu-central-1_.
Then try using this code instead of your adminDeleteUser function. Then it should work:
try {
const data = await cognitoidentityserviceprovider.adminDeleteUser(params).promise();
} catch (error) {
console.log(error);
}

Related

AWS-SNS publish() method not sending any messages to phone number

I have used aws-sdk in node express to send the verification code to the phone number I referred this docs to implement it. here I get the response but do not get any messages on the phone.
const AWS = require("aws-sdk");
AWS.config.update({
region: "region",
accessKeyId: "ACCESS_KEY",
secretAccessKey: "SECRET_KEY",
});
router.post("/", async (req, res) => {
let phone_no = "+91876214****";
let random = Math.floor(100000 + Math.random() * 900000);
const YOUR_MESSAGE = `Your verification code is ${random}`;
let params = {
Message: YOUR_MESSAGE,
PhoneNumber: phone_no,
MessageAttributes: {
"AWS.SNS.SMS.SMSType": {
DataType: "String",
StringValue: "Transactional",
},
};
let publishTextPromise = new AWS.SNS({ apiVersion: "2010-03-31" })
.publish(params)
.promise();
publishTextPromise
.then(function (data) {
return res.json({ id: data.MessageId, otp: random });
})
.catch(function (err) {
return res.send(err.stack);
});
});
is anything I'm doing wrong, new to this aws-sns concept.
here i logged the publishTextPromise i get response as Promise { <pending> }
If you get Success result from api but message is not received. You need to check SNS logs.
On SNS console, you can see Text messaging (SMS) under Mobile section. If you don't enable status logging, firstly edit preferences on Delivery status logs section then on new page create an IAM Role (SNSSuccessFeedback) with Success sample rate 100%.
You can find error on cloudwatch.
Potential problem. You account is in the SMS sandbox in the region which you used.
If you see any message like ☝️, You can add your phone number on sandbox destination phone number.
Then sandbox will send you a verify code, when you put it on AWS console. Then you will be able to receive messages from SNS.

How do I initiate a conversation with AWS LEX from node js?

My context is this: I am attempting to build a chat bot into my Mozilla Hubs client, which is a node js / React project. I have a lex bot created on AWS, and I have installed the client-lex-runtime-v2 package and can import it successfully, but I have no idea how to set up a StartConversationCommand, give it credentials, etc. Most of the javascript examples seem to go the other way, where Lex calls a lambda function after processing a request, but I have user input in my app and I need to send it to Lex, and then deliver the resulting text back to the user inside my application.
This seems like very basic Lex usage - if anyone could point me to a code example I would be most grateful.
John,
You need to make use of the LexRuntimeV2Client in the SDK as demonstrated here.
From the linked documentation, the below is how you import and instantiate a new client:
import { LexRuntimeV2Client, DeleteSessionCommand } from "#aws-sdk/client-lex-runtime-v2";
const client = new LexRuntimeV2Client({ region: "REGION" });
Once configured with your respective AWS environment details, credentials etc you will be able to invoke your Lex bot (again, from the linked documentation):
try {
const data = await client.send(command);
// process data.
} catch (error) {
// error handling.
} finally {
// finally.
}
Take a look at this sample repo on GitHub as well: aws-lex-web-ui
So for anyone else stuck where I was, I cannot say this is the right way to do it, because I never did get it working, but the closest I got to at least forming up my credentials and trying to make a connection was this:
const client = new LexRuntimeV2Client({
region: "us-east-1",
credentials: new AWS.Credentials({
accessKeyId: "my_IAM_access_key_id",
secretAccessKey: "my_secret_access_key"
})
});
const lexparams = {
"botAliasId": "my alias_id",
"botId": "bot_id_from_lex",
"localeId": "en_US",
"inputText": "hello, this is a test sample",
"sessionId": "some_session_id"
};
let cmd = new StartConversationCommand(lexparams);
try {
const data = await client.send(cmd);
console.log("Success. Response is: ", data.message);
} catch (err) {
console.log("Error responding to message. ", err);
}
As said, buyer beware, and best of luck out there! I hope this might help somebody in some slight way. We taking a momentary break on this problem until a member of our team with better aws fu can take a look at it. :-)
This is working for me:
var AWS = require('aws-sdk');
const { LexRuntimeV2 } = require("#aws-sdk/client-lex-runtime-v2");
const lexruntime = new LexRuntimeV2({
region: "us-west-2", // Set your Bot Region
credentials: new AWS.Credentials({
accessKeyId: "***", // Add your access IAM accessKeyId
secretAccessKey: "***" // Add your access IAM secretAccessKey
})
});
const lexparams = {
"botAliasId": "HG****", // Enter the botAliasId
"botId": "HG***", // Enter the botId
"localeId": "en_US",
"text": "Book Car",
"sessionId": "some_session_id"
};
lexruntime.recognizeText(lexparams, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});

Nodejs - AWS SNS publish is called, but message is not being sent

I'm trying to publish a SNS message to a single user.
The message is working when I manually press the "Publish Endpoint" button in the AWS console, but I'm trying to send it programmatically using the SNS nodejs SDK.
I have made sure to create a single IAM role giving full access permissions to SNS.
I have made sure to configure it:
const AWS = require("aws-sdk");
AWS.config.update({
region: process.env.AWS_REGION,
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
});
I first create a platform endpoint using sns.createPlatformEndpoint(endPointParams) and it works fine so my IAM role is not an issue.
Using the result from that call, I use the data to create publishParams to make a call right after creating the endpoint:
let payload = {
user : "Hihih test",
shopping_list : "shopping_item"
};
let endPointParams = {
PlatformApplicationArn: process.env.REACT_APP_SNS_ARN,
Token: req.body.device_token
}
const createEndPoint = sns.createPlatformEndpoint(endPointParams).promise();
createEndPoint.then(data => {
console.log(data.EndpointArn);
console.log(JSON.stringify(payload));
let publishParams = {
Message: JSON.stringify(payload),
MessageStructure: 'json',
TargetArn: data.EndpointArn
};
sns.publish(publishParams, (err, result1) =>{
console.log("Success");
});
return;
}).then(result => {
res.status(200).send("Success sending invite to user.");
}).catch(err => {
console.log(err);
res.status(500).send("Something went wrong sending the invite.");
});
});
The console.log("Success"); inside sns.publish() is being fired, but on the client side, the app does not receive a message. I have also tried multiple times to manually call "Publish Message" in the console and it works fine.
So what could my issue be? I think it's something wrong with my code.
When using SNS with GCM you need to structure your JSON payload with the keys GCM and default or it will throw an err.
var payload = {
default: 'Hello World',
GCM: {
notification: {
title: 'Hello World',
body: 'Notification Body'
// other configs can be put here
}
}
};

AWS SES service issue in sending mail using lambda

I have a Basic AWS account in which we have deployed a lambda function. Also we have configured AWS SES service within the lambda function to send email (also our SES service is moved out of the sandbox & limit increased).
Approximately we are sending two emails per minute but we found that rarely we are getting mail, but most of the time we are not getting any email.
Also we tried deploying the application in two region but we found none to be successful.
Sample code
const AWS = require('aws-sdk');
//AWS Options
const options = {
region: 'us-east-1',
// accessKeyId not required because of server less app (SWS policy added in role)
// secretAccessKey not required because of server less app (SWS policy added in role)
}
const ses = new AWS.SES(options);
const sendEmail = (sender, receivers, subject, content) => {
console.log("Sending From", sender);
console.log("REceiver Email", receivers);
const promise = new Promise((resolve, reject) => {
ses.sendEmail({
Source: sender,
Destination: {
ToAddresses: receivers
},
Message: {
Subject: {
Data: subject
},
Body: {
Html: {
Data: content
}
}
}
}, (err, data) => {
if (err) {
console.log(err)
reject(err)
}
resolve(data)
});
});
return promise
};
I think there are a couple of things going on here:
JavaScript functions that return promises need to be async
your Lambda function may be timing out (the default is 3 seconds)

Send a notification by Lambda function with AWS Pinpoint

I have an iOS app with a backend build by MobileHub with API Gateway, Lambda, and DynamoDB.
I noticed that the SNS function of MobileHub has been replaced by Pinpoint, and I would like to create a notification system using this new service.
When a user creates a new post through API Gateway, a lambda function will be triggered and I suppose I can send a notification to the subscribers by Pinpoint.
But I cannot find any example or reference doc in the official website of Pinpoint.
Do you have any resource for this scenario or any idea? Thank you very much!
Depends what you mean by notification, I assume you would like to send a push notification to a particular user (Pinpoint endpoint).
Pinpoint stores each device associated with a user as an "endpoint", generally created by the AWS client side analytics library (e.g. amplify analytics).
The client
With the amplify analytics library, I call updateEndpoint so I can specify a userId that is available to Lambda, as well as the device token and remove optOut so user can receive the push notification:
Address - The token generated from user accepting push notification permission (iOS)
optOut - NONE so they can receive push notifications
userId - unique id for user (Cognito's sub)
Lambda (node.js)
Now you can send a push notification, using the userId and the Pinpoint SDK.
Example:
const sendMessagesParams = {
ApplicationId: process.env.PINPOINT_APP_ID,
SendUsersMessageRequest: {
Users: {
[receiverUserId]: {}
},
MessageConfiguration: {
APNSMessage: {
Action: 'OPEN_APP',
Title: 'Message received',
SilentPush: false,
Body: `You have a new message`
},
GCMMessage: {
Action: 'OPEN_APP',
Title: 'Message received',
SilentPush: false,
Body: `You have a new message`
}
}
}
};
console.log('sendMessagesParams', JSON.stringify(sendMessagesParams));
pinpoint.sendUsersMessages(sendMessagesParams, (sendMessagesErr, sendMessagesData) => console.log('push sent')
For your particular scenario, I set up a DynamoDB stream and trigger a Lambda when a record changes within the table. You may need to add the IAM permissions manually once the lambda is created.
Sources
Full list of pinpoint methods you can use in lambda (Node.JS)
Update endpoint using Amplify Analytics (JS)
Dynamodb streams - trigger a lambda
I was struggling a lot getting a lambda function working, so please see this answer as an addition to the answer from Dylan w.
Client
import PushNotification from '#aws-amplify/pushnotification';
import Analytics from '#aws-amplify/analytics';
PushNotification.onRegister((token) => {
Analytics.updateEndpoint({
address: token,
channelType: 'APNS',
optOut: 'NONE',
// Customized userId
userId: "e236e3ea-bas9-4eae-967e-0eb9bcaca26d" // Example
})
});
Lambda function
'use strict';
const AWS = require('aws-sdk');
exports.handler = async (event, context) => {
var pinpoint = new AWS.Pinpoint();
const sendMessagesParams = {
ApplicationId: <YOUR_APPLICATION_ID>, // Find it in Pinpoint->All projects
SendUsersMessageRequest: {
Users:{<USER_ID>:{}}, // The same userId as set on the client. This way you can "follow" people if they switch device
MessageConfiguration:{
APNSMessage:{
Action:"OPEN_APP",
Title:"Message received",
Body:"You have a new message"
}
}
}
};
return await new Promise( (resolve, reject) => {
pinpoint.sendUsersMessages(sendMessagesParams, (sendMessagesErr, sendMessagesData) => {
if(sendMessagesErr) reject(sendMessagesErr)
if(sendMessagesData) resolve(sendMessagesData)
});
});
};
Note that the call to pinpoint is wrapped in a promise. Because pinpoint.sendUserMessages accepts a callback, the execution is continued (async nature of Node), which will shut down the lambda function and you will get no output from the callback function or receive a notification, without awaiting the function to finish.
Finally, I've got something that perfectly works.
The answer is that you have to use "targetClient" to update endpoint inside "didRegisterForRemoteNotificationsWithDeviceToken" function.
let client = self.pinpoint!.targetingClient
let profile = client.currentEndpointProfile()
print("EndpointId = \(profile.endpointId)")
profile.user?.userId = <YOUR_CUSTOM_ID>
client.update(profile)
Client Side(XCODE)
Here is my [AppDelegate.swift] looks like:
(Important part is inside "didRegisterForRemoteNotificationsWithDeviceToken" function)
import UserNotifications
import AWSPinpoint
class AppDelegate: UIResponder, UIApplicationDelegate {
var pinpoint: AWSPinpoint?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Instantiate Pinpoint
let pinpointConfiguration = AWSPinpointConfiguration.defaultPinpointConfiguration(launchOptions: launchOptions)
// Set debug mode to use APNS sandbox, make sure to toggle for your production app
pinpointConfiguration.debug = true
self.pinpoint = AWSPinpoint(configuration: pinpointConfiguration)
// Present the user with a request to authorize push notifications
self.registerForPushNotifications()
return true
}
func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
print("Permission granted: \(granted)")
guard granted else { return }
// Only get the notification settings if user has granted permissions
self?.getNotificationSettings()
}
}
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
// Register with Apple Push Notification service
UIApplication.shared.registerForRemoteNotifications()
}
}
}
func application(_: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("DidRegisterForRemoteNotificationsWithDeviceToken: Start")
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
// Register the device token with Pinpoint as the endpoint for this user
self.pinpoint!.notificationManager.interceptDidRegisterForRemoteNotifications(withDeviceToken: deviceToken)
//set custom userId and update endpoint
let client = self.pinpoint!.targetingClient
let profile = client.currentEndpointProfile()
print("EndpointId = \(profile.endpointId)")
profile.user?.userId = <YOUR_CUSTOM_ID>
client.update(profile)
}
func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("didReceiveRemoteNotification:\(userInfo)")
// Pass this remote notification event to pinpoint SDK to keep track of notifications produced by AWS Pinpoint campaigns.
self.pinpoint!.notificationManager.interceptDidReceiveRemoteNotification(userInfo)
}
}
Backend (Lambda with Nodejs)
And here is a code to send a notification to specific user.
'use strict';
const AWS = require('aws-sdk');
const pinpoint = new AWS.Pinpoint({region: 'us-west-2'});
exports.handler = (event, context, callback) => {
const done = (err, data) => {
if(err){
console.log('ERROR:', err);
const response = {
statusCode: 400,
body: JSON.stringify(err)
};
callback(response);
}else{
console.log('SUCCESS:', data);
const response = {
statusCode: 200,
body: JSON.stringify(data)
};
callback(null, response);
}
};
let users = {};
users[<YOUR_CUSTOM_ID>] = {};
const params = {
ApplicationId: PINPOINT_PROJECT_ID,
SendUsersMessageRequest: {
Users: users,
MessageConfiguration: {
APNSMessage: {
Action: 'OPEN_APP',
Title: "Hi, I am AWS Pinpoint.",
SilentPush: false,
Body: "You've got a nice message."
}
}
}
};
pinpoint.sendUsersMessages(params, (err, data)=>{
if(err){
done(err);
}else{
done(null, data);
}
});
};
Hope those work for you too.
This is certainly possible with Amazon Pinpoint. You can find the Javascript SDK documentation here.
There are 2 modes of sending with Pinpoint.
Direct send - This is effectively the same as what SNS has traditionally been. You need a device token and you can send directly to your push provider using that token.
Segmentation sends - This mode is slightly different and assumes that you have loaded all your devices into Pinpoint via the Mobile SDK as part of your app, or via an S3 Import. The benefit here is that you can segment your devices and send to that segment (e.g. 'Ryans Friends').
So in your Lambda backed API you can choose to either send directly to subscribers (if you have their addresses) or potentially an entire segment of subscribers (if you have loaded your endpoints into Pinpoint).

Resources