Cognito authorizing a user through AWS lambda function - amazon-web-services

I use AWS Cognito and need to authorize a user through a lambda function. I have seen examples online and when I try to apply them, the Cognito authentication does not run and gets somehow skipped:
const AWS = require('aws-sdk');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
global.fetch = require("node-fetch");
const CognitoUserPool = AmazonCognitoIdentity.CognitoUserPool;
var AuthenticationDetails = AmazonCognitoIdentity.AuthenticationDetails;
var CognitoUser = AmazonCognitoIdentity.CognitoUser;
var USER_POOL_ID = 'my_pool_id';
var CLIENT_ID = 'my_client_id';
var idToken = '';
exports.handler = async (event, callback) => {
var email = event['username'];
var password = event['password'];
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails({
Username: email,
Password: password
});
const poolData = {
UserPoolId: USER_POOL_ID,
ClientId: CLIENT_ID
};
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username: email,
Pool: userPool
}
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (result) => {
var accessToken = result.getAccessToken().getJwtToken();
console.log(result);
console.log(accessToken);
idToken = result.idToken.jwtToken;
console.log(idToken);
callback(null, accessToken);
},
onFailure: (err) => {
console.log(err);
idToken = err;
callback(err);
},
});
console.log("cognitoUser after: ", cognitoUser);
};
I can see the last console.log printed in the logs, but lambda does not seem to wait for the request resolution of cognitoUser.authenticateUser, as none of the console.logs inside onSuccess or onFailure get printed.

Here are couple of options
Remove async from exports.handler = async (event, callback).
Keep async and wrap authenticateUser as Promise and use await
const res = await new Promise((resolve, reject) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (result) => {
var accessToken = result.getAccessToken().getJwtToken();
console.log(result);
console.log(accessToken);
idToken = result.idToken.jwtToken;
console.log(idToken);
resolve(accessToken);
},
onFailure: (err) => {
console.log(err);
idToken = err;
reject(err);
},
});
}
Note: Code has not been tested.

Related

Running from AWS Lambda function: can see CloudWatch logs, except in axios's continuation

Running the code below in AWS Lambda, I can see in CloudWatch the first part (Sending out to: ${url}), but not the part in axios's .then:
console.log(`Sending out to: ${url}`);
axios.post(url, 'Hi from Lambda!')
.then((response) => {
console.log("Axios succeeded:", response);
}, (error) => {
console.log("Axios failed:", error);
});
Am I missing something? why isn't the response/error visible in CloudWatch?
(I know that axios is working because when I'm using an incorrect address in url, I'm getting e.g. an ECONNREFUSED error)
Edit - full code:
const axios = require('axios');
exports.handler = async (event, connection, publish) => {
console.log(`The event:`, event);
console.log(`Connection:`, connection);
var domainName = event.requestContext.domainName;
var stage = event.requestContext.stage;
var connectionId = event.requestContext.connectionId;
var url = `https://${domainName}/${stage}/#connections/${connectionId}`;
console.log(`Sending out to: ${url}`);
axios.post(url, 'Hi from Lambda!')
.then((response) => {
console.log("Axios succeeded:", response);
}, (error) => {
console.log("Axios failed:", error);
});
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
Your function finished right after Axios request is called. This means the function does not wait until the request complete.
You write the lambda as an async function, then just use await to wait for the Axios request.
const axios = require('axios');
exports.handler = async (event, context) => { // event and context
console.log(`The event:`, event);
console.log(`Connection:`, context);
var domainName = event.requestContext.domainName;
var stage = event.requestContext.stage;
var connectionId = event.requestContext.connectionId;
var url = `https://${domainName}/${stage}/#connections/${connectionId}`;
console.log(`Sending out to: ${url}`);
const response = await axios.post(url, 'Hi from Lambda!') // wait until request response
.catch(error => { // If you don't care about the error, just print it out an continue
console.log("Axios failed:", error);
});
console.log("Axios succeeded:", response);
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
};

aws cognito redirect after user confirmation

i have followed all the solution and answer from this stack overflow question
How to redirect after confirm amazon cognito using confirmation URL?
but I am getting this error
{"errorType":"TypeError","errorMessage":"Cannot read property 'code' of undefined","trace":["TypeError: Cannot read property 'code' of undefined"," at Runtime.module.exports.handler (/var/task/index.js:10:54)"," at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"]}
code of customize message lambda is:
exports.handler = (event, context, callback) => {
// Identify why was this function invoked
if(event.triggerSource === "CustomMessage_SignUp") {
console.log('function triggered');
console.log(event);
// Ensure that your message contains event.request.codeParameter. This is the placeholder for code that will be sent
const { codeParameter } = event.request
const { userName, region } = event
const { clientId } = event.callerContext
const { email } = event.request.userAttributes
const url = 'https://xxxxxxxxx.execute-api.ap-southeast-1.amazonaws.com/confirm'
const link = `here`
event.response.emailSubject = "Your verification link"; // event.request.codeParameter
event.response.emailMessage = `Thank you for signing up. Click ${link} to verify your email.`;
}
// Return to Amazon Cognito
callback(null, event);
};
code for redirect lambda is
'use strict';
var AWS = require('aws-sdk');
AWS.config.setPromisesDependency(require('bluebird'));
var CognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-19', region: process.env.REGION });
module.exports.handler = (req, context, callback) => {
console.log('req');
console.log(req);
const confirmationCode = req.queryStringParameters.code
const username = req.queryStringParameters.username
const clientId = req.queryStringParameters.clientId
const region = req.queryStringParameters.region
const email = req.queryStringParameters.email
let params = {
ClientId: clientId,
ConfirmationCode: confirmationCode,
Username: username
}
var confirmSignUp = CognitoIdentityServiceProvider.confirmSignUp(params).promise()
confirmSignUp.then(
(data) => {
let redirectUrl = process.env.POST_REGISTRATION_VERIFICATION_REDIRECT_URL;
const response = {
statusCode: 301,
headers: {
Location: redirectUrl,
}
};
return callback(null, response);
}
).catch(
(error) => {
callback(error)
}
)
}
customize_message lambda is triggered by the aws cognito which send
code for verification
redirect lambda is triggered by the API gateway
please help
thank you in advance
sometihing wrong with access your req parameter. try to use this code
'use strict';
var AWS = require('aws-sdk');
AWS.config.setPromisesDependency(require('bluebird'));
var CognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-19', region: process.env.REGION });
module.exports.handler = (req, context, callback) => {
console.log('req');
console.log(req);
const confirmationCode = req.code;
const username = req.username;
const clientId = req.clientId;
const region = req.region;
const email = req.email;
let params = {
ClientId: clientId,
ConfirmationCode: confirmationCode,
Username: username
}
var confirmSignUp = CognitoIdentityServiceProvider.confirmSignUp(params).promise()
confirmSignUp.then(
(data) => {
let redirectUrl = process.env.POST_REGISTRATION_VERIFICATION_REDIRECT_URL;
const response = {
statusCode: 301,
headers: {
Location: redirectUrl,
}
};
return callback(null, response);
}
).catch(
(error) => {
callback(error)
}
)
}
instead of your code.
only change is instead of all this
const confirmationCode = req.queryStringParameters.code;
const username = req.queryStringParameters.username;
const clientId = req.queryStringParameters.clientId;
const region = req.queryStringParameters.region;
const email = req.queryStringParameters.email;
you need to use this
const confirmationCode = req.code;
const username = req.username;
const clientId = req.clientId;
const region = req.region;
const email = req.email;

AWS sts assumeRole fails for cross account api access on second request onwards

I am trying to access cross-account API in AWS using the assumed role. The roles are configured correctly.
I am able to invoke API for the first and after that till the session duration, I am getting something like the following error. I am not sure how to handle this to make every request success. Please help.
Error: AccessDenied: User: arn:aws:sts::xxxxxxxxxx:assumed-role is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::xxxxx:role/assume-role
const AWS = require('aws-sdk')
var aws4 = require('aws4');
const REGION = process.env.REGION || 'eu-west-1';
const ASSUME_ROLE_ARN = process.env.ASSUME_ROLE_ARN || '';
const API_ID_PARAMETER = process.env.EXT_API_ID_PARAMETER || '/test/gl/module/dev/testapi';
exports.handler = async(event) => {
console.log('received event')
console.log('ASSUME_ROLE_ARN:' + ASSUME_ROLE_ARN);
console.log('API_ID_PARAMETER:' + EXT_API_ID_PARAMETER);
var sts = new AWS.STS({
region: REGION
});
console.log('sts success')
const getCrossAccountCredentials = async() => {
return new Promise((resolve, reject) => {
const timestamp = (new Date()).getTime();
const params = {
RoleArn: 'arn:aws:iam::XXXXXXXXXXX:role/assume-role',
RoleSessionName: `be-descriptibe-here-${timestamp}`,
DurationSeconds: 3600
};
console.log('RoleSessionName : ' + params.RoleSessionName)
sts.assumeRole(params, (err, data) => {
if (err)
reject(err);
else {
resolve({
accessKeyId: data.Credentials.AccessKeyId,
secretAccessKey: data.Credentials.SecretAccessKey,
sessionToken: data.Credentials.SessionToken,
});
}
});
});
}
try {
const tempdata = await getCrossAccountCredentials();
var data = JSON.parse(JSON.stringify(tempdata));
console.log(data);
console.log(data.accessKeyId);
console.log(data.secretAccessKey);
AWS.config.update({
accessKeyId: data.accessKeyId,
secretAccessKey: data.secretAccessKey,
sessionToken: data.sessionToken
});
const creds = {
accessKeyId: data.accessKeyId,
secretAccessKey: data.secretAccessKey,
sessionToken: data.sessionToken
};
console.log('AWS.SSM start')
var ssm = new AWS.SSM({
apiVersion: '2014-11-06',
region: REGION,
credentials: creds
});
console.log('AWS.SSM end')
const {
Parameter
} = await ssm
.getParameter({
Name: EXT_API_ID_PARAMETER
})
.promise();
console.log(Parameter.Value)
let request = {
host: `${Parameter.Value}.execute-api.eu-west-1.amazonaws.com`,
method: 'GET',
url: `https://${Parameter.Value}.execute-api.eu-west-1.amazonaws.com/dev/test/1`,
path: '/dev/test/1'
};
let signedRequest = aws4.sign(request, creds);
delete signedRequest.headers['Host'];
delete signedRequest.headers['Content-Length'];
const res = await axios(signedRequest);
console.log('Data:', res.data);
} catch (err) {
console.log('Error:', err);
}
};

AWS SES send email lambda not sending every time

I want to send emails using the ses from aws from lambda. The problem is that the email is only sent some times using the same code. We don't get errors.
Here's the code:
const AWS = require('aws-sdk');
var ses = new AWS.SES();
exports.handler = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
await new Promise((resolve, reject) => {
var params = {
Destination: {
ToAddresses: [myEmail]
},
Message: {
Body: {
Text: { Data: "Test"
}
},
Subject: { Data: "Test Email"
}
},
Source: "sourceMail"
};
ses.sendEmail(params, function (err, data) {
if (err) {
console.log(err);
context.fail(err);
} else {
console.log(data);
context.succeed(event);
}
callback(null, {err: err, data: data});
});
});
}
I would be careful with using callbackWaitsForEmptyEventLoop as it can lead to unexpected results (If this is false, any outstanding events continue to run during the next invocation.).
Can you try using this simplified version:
const AWS = require('aws-sdk');
var ses = new AWS.SES();
exports.handler = async (event, context, callback) => {
const params = {
Destination: {
ToAddresses: [myEmail],
},
Message: {
Body: {
Text: { Data: 'Test' },
},
Subject: { Data: 'Test Email' },
},
Source: 'sourceMail',
};
await ses.sendEmail(params).promise();
return event;
};

Token received in lambda function returns Unauthorized in APIG

These are my first steps in AWS in general and Cognito specifically. So please bear with me as I'm a bit confused by all the concepts and documention is not very easy to follow.
So I set up 3 lambda functions, one that creates a user, one that confirms a user and a last one that is supposed to authenticate the user.
The first 2 work fine, my user is created an confirmed. Now I'm stuck with the 3rd one which is supposed to return a token to be used in APIG, where I've set up a simple endpoint with my cognito authorizer.
Every token I get back returns Unauthorized when tested in the APIG/Authorizers/Cognito Pool Authorizers section.
My 'sign in' code is the following:
const AWS = require('aws-sdk');
exports.handler = (event, context, callback) => {
AWS.config.apiVersions = {
cognitoidentityserviceprovider: '2016-04-18'
};
AWS.config.region = 'us-east-1'; // Region
/*AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'MY_IDENTITY_POOL_ID',
});*/
var identityId = null;
var params = {
IdentityPoolId: 'MY_IDENTITY_POOL_ID',
IdentityId: identityId,
Logins: {
'login.auth.MYPROJECT': 'MY_USERNAME'
},
TokenDuration: 86400
};
var cognito = new AWS.CognitoIdentity({
region: AWS.config.region
});
cognito.getOpenIdTokenForDeveloperIdentity(params, function(err, data) {
if (err) {
return callback(err);
}
else {
/*AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: params.IdentityPoolId
});*/
AWS.config.credentials.get(function(){
// Credentials will be available when this function is called.
var accessKeyId = AWS.config.credentials.accessKeyId;
var secretAccessKey = AWS.config.credentials.secretAccessKey;
var sessionToken = AWS.config.credentials.sessionToken;
callback(null, {
identityId: data.IdentityId,
token: data.Token,
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey,
sessionToken: sessionToken
});
});
}
});
}
Both token and sessionToken return Unauthorized. Can someone tell me what is missing here?
Much appreciated.
EDIT 2016-11-15
The 'register' lambda code:
const AWS = require('aws-sdk');
exports.handler = (event, context, callback) => {
AWS.config.region = 'us-east-1'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'MY_IDENTITY_POOL_ID',
});
var poolData = {
UserPoolId : 'MY_USER_POOL_ID',
ClientId : 'MY_CLIENT_ID'
};
var userPool = new AWS.CognitoIdentityServiceProvider(poolData);
var email = "myemail+" + Math.floor(Math.random() * (100 - 1) + 1) + "#example.com";
var params = {
ClientId: 'MY_CLIENT_ID',
Password: '1234567890',
Username: 'testaccount' + Math.floor(Math.random() * (100 - 1) + 1),
UserAttributes: [
{
Name: 'email',
Value: email
}
]
};
userPool.signUp(params, function(err, result){
if (err) {
console.log(err)
return;
}
callback(null, {
"message": "Hello from Lambda",
"data": result
});
});
};
My 'activate' lambda code is the following:
const AWS = require('aws-sdk');
exports.handler = (event, context, callback) => {
AWS.config.region = 'us-east-1'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'MY_IDENTITY_POOL_ID',
});
var poolData = {
UserPoolId : 'MY_USER_POOL_ID',
ClientId : 'MY_CLIENT_ID'
};
var userPool = new AWS.CognitoIdentityServiceProvider(poolData);
var email = "email_address#example.com";
var params = {
ClientId: 'MY_CLIENT_ID',
Username: 'test_username',
ForceAliasCreation: false,
ConfirmationCode: '927000'
};
userPool.confirmSignUp(params, function(err, result){
if (err) {
console.log(err)
return;
}
callback(null, {
"message": "Hello from Lambda",
"data": result
});
});
};
In APIG, I created a Cognito User Pool Authorizer, selected my user pool, gave it a name, and set the identity token source to 'method.request.header.Authorization'.
In my APIG resource under the Method Request, I've set Authorization to my Cognito User Pool Authorizer. Additionally, API Key Required is set to true and I've a couple of keys I was testing with and that caused no issues.
I hope this covers everything.