send email when user registers - AWS Cognito federated Identities - amazon-web-services

How can i send an email/trigger a lambda function when a new user registers?
Under "edit identity pool" i only found a sync trigger.
If i understand correctly: This one is triggered every time a user syncs his data...
Is there any way to trigger a lambda function only for the "initial" sync or when a certain dataset is created for the user?
Edit:
To be more specific: I do create the user via lambdas using the JS SDK. I use developer authentication with my own oauth2 flow. I don't know how to distinguish between a user granting access e.g. via Google the first time from someone doing this the second time. The json with the access code seams the same to me... Maybe I am mistaken.
Also using the getOpenIdTokenForDeveloperIdentity call I don't know how to distinguish between an ID that is new to cognito from one cognito already knows.
Edit 2:
To be even more precise:
I am building on this project: https://github.com/laardee/serverless-authentication-boilerplate/blob/master/authentication/lib/storage/usersStorage.js
here is how i do save the User to cognito at the moment.
I do run this code for first time users as well as nth time users. My problem is that i dont know how to distinguish...
const saveCognito = (profile) => new Promise((resolve, reject) => {
if (profile) {
cognitoidentity.getOpenIdTokenForDeveloperIdentity({
IdentityPoolId: process.env.COGNITO_IDENTITY_POOL_ID,
Logins: {
// profile.userId = encrypted id of the e.g. google oauth2 id
[process.env.COGNITO_PROVIDER_NAME]: profile.userId
}
}, (err, dat) => {
if (err) {
reject(err);
} else {
var list_params = {
DatasetName: 'user-data', /* dataset name */
IdentityId: dat.IdentityId, /* cognito id */
IdentityPoolId: process.env.COGNITO_IDENTITY_POOL_ID
};
cognitosync.listRecords(list_params, function(err, data) {
if (err) {
reject(err); // an error occurred
} else {
var RecordPatches = //[Parts of the i want to write to the user]
// SyncSessionToken is returned by the cognitosync.listRecords call
list_params["SyncSessionToken"] = data.SyncSessionToken;
list_params["RecordPatches"] = RecordPatches;
cognitosync.updateRecords(list_params, function(err, update_data) {
if (err){
reject(err);
} else {
resolve();
}
});
}
});
}
});
} else {
reject('Invalid profile');
}
});

So this is something which is not currently supported in Cognito out of the box. You are correct in saying that the only built in Cognito Event that will trigger a Lambda Function is the "Sync Trigger" Event. This Sync event is fired every time that a Cognito IdentityId Synchronizes some of their data to the Cognito Sync cloud data store.
This event is unrelated to the creation of a new IdentityId by Cognito Federated Identity.
You could in theory:
Run a list-identities call on the IdentityPool, before the user logs
in.
Login the user. Check whether the IdentityId which has been given to the user is present in the list you retrieved before they logged
in. This would tell you whether or not the identity that they were
given existed before this login.
Based on this information you could
make a decision whether or not to programatically call the Lambda
Function from your application.
The setup of the above would be complex, as for security reasons you would need to maintain this service, server-side. The list-identities call requires AWS credentials to call. And I doubt you'd want to include permissions for that call in your IAM policy for unauthenticated users.
Aside from the above there is not much you can do at the moment.
In order to do this, you would need to setup a DynamoDB table (or some similar low latency datastore) where you could maintain the state of the IdentityId list, and then query this service/store whenever you login a user to compare new logins to the pre-existing list.
If this is critical to your use case I would suggest heading over to AWS Support, and create a case where you can log this as a feature request.
https://aws.amazon.com/premiumsupport/

Related

GCP cloud build VIEW RAW logs link

I have written a small cloud function in GCP which is subscribed to Pub/Sub event. When any cloud builds triggered function post message into the slack channel over webook.
In response, we get lots of details to trigger name, branch name, variables details but i am more interested in Build logs URL.
Currently getting build logs URL in response is like : logUrl: https://console.cloud.google.com/cloud-build/builds/899-08sdf-4412b-e3-bd52872?project=125205252525252
which requires GCP console access to check logs.
While in the console there an option View Raw. Is it possible to get that direct URL in the event response? so that i can directly sent it to slack and anyone can access direct logs without having GCP console access.
In your Cloud Build event message, you need to extract 2 values from the JSON message:
logsBucket
id
The raw file is stored here
<logsBucket>/log-<id>.txt
So, you can get it easily in your function with Cloud Storage client library (preferred solution) or with a simple HTTP Get call to the storage API.
If you need more guidance, let me know your dev language, I will send you a piece of code.
as #guillaume blaquiere helped.
Just sharing the piece of code used in cloud function to generate the singedURL of cloud build logs.
var filename ='log-' + build.id + '.txt';
var file = gcs.bucket(BUCKET_NAME).file(filename);
const getURL = async () => {
return new Promise((resolve, reject) => {
file.getSignedUrl({
action: 'read',
expires: Date.now() + 76000000
}, (err, url) => {
if (err) {
console.error(err);
reject(err);
}
console.log("URL");
resolve(url);
});
})
}
const singedUrl = await getURL();
if anyone looking for the whole code please follow this link : https://github.com/harsh4870/Cloud-build-slack-notification/blob/master/singedURL.js

Getting - UserNotFoundException - while calling adminCreateUser - AWS Cognito

I am having a very peculiar issue regarding User creation in AWS Cognito.
When I am in the same region (e.g US-East-AWS and Creating User from NA) then it is working like a charm. But when someone is in other region (e.g US-EAST-AWS and creating user from EU with a slow internet connection) then I am getting UserNotFoundException.
Below is the how I am handling User creation in my Lambda function (JS)
await cognitoidentityserviceprovider.adminCreateUser(params).promise()
if (customer.MFA === true) {
await toggleCognitoUsersMFA(jsonBody.email, true)
}
Check your MessageAction value in your params.
If the value is set to RESEND, and you're creating a new user, you'll get this User Not Found message.
The current AWS docs indicate that either a RESEND or SUPPRESS value is required, this is not true, you can actually omit the value.

Is it possible to make AWS Websocket + Lambda function to constant monitoring of the DynamoDB and send response to the client?

I have a serverless project: AWS + Angular on the frontend. Currently, I get the data when page is initialized and refresh the data when press "update" button. However, I want to monitor changes in the table constantly. In Firebase there is onSnapShot() method, which sends the new data when a collection is updated.
I want to make something similar with AWS. However, in official documentation, I do not see how to correctly do it.
So here are 2 questions:
How can I connect to the WebSocket with aws-sdk? (Currently, I can connect only from the terminal with wscat -c myurl call. Or shall I simply send http.Post with websocket url?
is it possible to pass invoke in the callback URL? - I want to get data from DynamoDB when page initialize and then invoke it again and again (with a callback URL)
My Lambda function looks like this:
exports.handler = async (event, context) => {
let params = {
TableName: "documents"
}
let respond = await db.scan(params).promise();
return respond;
};
On the front-end I have:
ngOnInit(): void {
AWS.config.credentials = new AWS.Credentials({
accessKeyId: '//mykey', secretAccessKey: '//mysecretkey'
})
AWS.config.update({
region:'//myregion'
})
this.updateTable() // triggers post request to APi Gateway => lambda and receives a response with data.
}
From my understanding, you will need to set up a DynamoDB stream and a lambda function that respond to the database CRUD events, send the updated data to the WebSocket connection if the event data matches the criteria (document id for example), through AWS.ApiGatewayManagementApi. (FYI: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ApiGatewayManagementApi.html)

AWS Lambda for Client to Client Push Notifications

Thanks to this community I've learned that is possible to send AWS SNS Push notifications via Lambda with node.js (as a result of Parse migration). I am still struggling with the following:
Can this be done client to client x likes y's z. Where x is user 1, y is user 2 and z is the object being liked? If so, it seems like Cognito is not required that it can read directly from the database but is that accurate?
Does anyone have an example of how this was implemented?
Again, we don't want to broadcast to all users on a schedule but rather when a client performs an action.
Thanks so much in advance!
Let's say you have Device1 which creates a piece of content. That is distributed to a number of users. Device2 receives this content and "likes" it.
Assumption:
you have registered for push notifications on the device, and created a SNS endpoint on AWS. You have stored that endpoint ARN in your database, and associated it with either the Cognito Id, or the content Id. If your data is normalized, then you'd typically have the SNS endpoint associated with the device.
Your Lambda will need to have access to that data source and look up that SNS endpoint to send push notifications to. This will depend on what sort of data store you are using (RDS, DynamoDB, something else). What sort of access that is, and how you secure it is a whole other topic.
From your Lambda, you fetch the ARN to send the push notification to. If you pass in the content Id from the Like, and have the Cognito Id from the device that Liked it, you can then look up the information you need. You then construct a SNS payload (I'm assuming APNS in this example), then send it off to SNS.
var message = {
"default": "New Like!",
"APNS": {
"aps": {
"alert": "New Like!"
}
}
};
var deviceParams = {
Message: JSON.stringify(message),
Subject: "New Like",
TargetArn: targetArn,
MessageStructure: "json"
};
self.sns.publish(deviceParams, function (err) {
if (err) {
console.error("Error sending SNS: ", err);
}
});
It's not all done for you like it might be with Parse. You need to work a lot harder on AWS, but you have near unlimited power to do what you want.
If this is a bit too much, you may want to consider Google's newly updated Firebase platform. It's very Parse-like: https://firebase.google.com/
Hope that helps you move forward a bit.
Further reading:
http://docs.aws.amazon.com/sns/latest/dg/mobile-push-apns.html
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/TheNotificationPayload.html
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html

Refresh of AWS.config.credentials

I'm trying to find a proper way of refreshing the AWS.config.credentials I get from Cognito. I'm using developer authorized identities and it works fine. I get the credentials and if I perform a refresh() the AWS.config.credentials.expireTime is also updated, as expected.
The credentials expire after one hour, so I thought I could use setTimeout to refresh the credentials and configure it based on the credentials.expireTime (I calculate the number of millis).
However, it seems like I have to perform the refresh much more often. The credentials keeps timing out before their time. The setTimeout-method works just fine if I reduce the delay to a much smaller amount, but I would prefer not to overdo the refresh.
Is this true, and if so how often do I need to do this? Having it refresh every 5 minutes or so seems excessive :/
Recurring refresh
function refreshAwsCredentials() {
clearTimeout(awsRenewalTimeout);
// perform credentials renewal
AwsService.refreshAwsCredentials()
.then( function () {
awsRenewalTimeout = setInterval(
function () {
refreshAwsCredentials();
}, AWS.config.credentials.expireTime.getTime() - new Date().getTime() - 300000
);
})
.catch( function (error) {
// checks error, normally it basically logs in, then refreshes
});
}
AwsService.refreshAwsCredentials()
if ( AWS.config.credentials.needsRefresh() ) {
AWS.config.credentials.refresh( function (error) {
if (error) {
// rejects promise with error message
}
else {
// resolves promise
}
});
}
I finally found out that I had a check for auth error where I was too specific. I checked for being logged in and not that the credentials had timed out. Now I attempt a login when things fail and the credential timeout is now resolved when needed.