How to use assume role credential in dynamodb (aws-sdk javascript)? - amazon-web-services

I already have aws assume role credentials in .aws/credetials file.
how to use it to creat sts or dynamodb like:
const { DynamoDB } = require('aws-sdk');
const { DocumentClient } = DynamoDB;
const dynamo = new DynamoDB({
endpoint: process.env.AWS_ENDPOINT,
region: process.env.AWS_REGION,
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
secretToken: process.env.aws_security_token
});
I mean I got error as:
root#myubuntu:~/work/contacts_api# node ./seed/runner.js
```
Checking if 'contacts' table exists
{ UnrecognizedClientException: The security token included in the request is invalid.
at Request.extractError (/root/work/contacts_api/node_modules/aws-sdk/lib/protocol/json.js:51:27)
at Request.callListeners (/root/work/contacts_api/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/root/work/contacts_api/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/root/work/contacts_api/node_modules/aws-sdk/lib/request.js:683:14)
at Request.transition (/root/work/contacts_api/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/root/work/contacts_api/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /root/work/contacts_api/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request. (/root/work/contacts_api/node_modules/aws-sdk/lib/request.js:38:9)
at Request. (/root/work/contacts_api/node_modules/aws-sdk/lib/request.js:685:12)
at Request.callListeners (/root/work/contacts_api/node_modules/aws-sdk/lib/sequential_executor.js:116:18)
message: 'The security token included in the request is invalid.',
code: 'UnrecognizedClientException',
time: 2019-01-07T05:39:54.907Z,
requestId: 'A5CFV62P0TGHJH7VDIBSL0JRC3VV4KQNSO5AEMVJF66Q9ASUAAJG',
statusCode: 400,
retryable: false,
retryDelay: 5.013458338738063 }
```
I want to know the correct way to initial credetials, if I want to use mfa credetials.

I'm guessing that the error here should give you a clue:
"The security token included in the request is invalid"
Did you try printing out the environment value
env | grep aws_security_token
If it's empty you'll have to set the value prior to running your code.
Also, I've noticed that your other aws keys are all caps whereas your aws_security_token is all lowercase.

I suspect secretToken isn't a thing. Here are two examples of how it could be done (how I've done it before).
That said I would encourage the construction and use of a Credentials where ever possible (the second example), but if you wanted to do it inline- that should work too.
/** assume a role and build a DocumentClient object to make a single scan **/
;(async () => {
const sts = new AWS.STS()
const assumeRole = await sts
.assumeRole({
RoleArn: process.env.ROLE_ARN,
RoleSessionName: process.env.ROLE_SESSION_NAME,
})
.promise()
const dynamodb = new AWS.DynamoDB.DocumentClient({
region: process.env.REGION,
credentials: {
accessKeyId: assumeRole.Credentials?.AccessKeyId,
secretAccessKey: assumeRole.Credentials?.SecretAccessKey,
sessionToken: assumeRole.Credentials?.SessionToken,
},
})
const scan = await dynamodb
.scan({
TableName: process.env.TABLE_NAME,
})
.promise()
console.log(scan)
})()
/**
* assume a role and build a Credentials object and use it
* to build a DocumentClient object to make a single scan
**/
;(async () => {
const sts = new AWS.STS()
const assumeRole = await sts
.assumeRole({
RoleArn: process.env.ROLE_ARN,
RoleSessionName: process.env.ROLE_SESSION_NAME,
})
.promise()
const credentials = new AWS.Credentials({
accessKeyId: assumeRole.Credentials?.AccessKeyId,
secretAccessKey: assumeRole.Credentials?.SecretAccessKey,
sessionToken: assumeRole.Credentials?.SessionToken,
})
const dynamodb = new AWS.DynamoDB.DocumentClient({
region: process.env.REGION,
credentials: credentials,
})
const scan = await dynamodb
.scan({
TableName: process.env.TABLE_NAME,
})
.promise()
console.log(scan)
})()

Related

Make a cross account call to Redshift Data API

Summary of problem:
We have an AWS Redshift cluster in Account A, this has a database called 'products'
In Account B we have a lambda function which needs to execute a SQL statement against 'products' using the Redshift Data API
We have setup a new secret in AWS Secrets manager containing the redshift cluster credentials. This secret has been shared with Account B. We've confirmed Account B can access this information from AWS Secrets Manager.
When we call the Redshift Data API action 'executeStatement' we get the following error:
ValidationException: Cluster doesn't exist in this region.
at Request.extractError (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\protocol\json.js:52:27)
at Request.callListeners (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\sequential_executor.js:106:20)
at Request.emit (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\sequential_executor.js:78:10)
at Request.emit (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\request.js:688:14)
at Request.transition (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\request.js:22:10)
at AcceptorStateMachine.runTo (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\state_machine.js:14:12)
at C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\state_machine.js:26:10
at Request.<anonymous> (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\request.js:38:9)
at Request.<anonymous> (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\request.js:690:12)
at Request.callListeners (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\sequential_executor.js:116:18)
The error message suggest it's perhaps not going to the correct account, since the secret contains this information I would have expected it to know.
Code Sample:
Here's my code:
var redshiftdata = new aws.RedshiftData({ region: 'eu-west-2'});
const params : aws.RedshiftData.ExecuteStatementInput = {
ClusterIdentifier: '<clusteridentifier>',
Database: 'products',
SecretArn: 'arn:aws:secretsmanager:<region>:<accountNo>:secret:<secretname>',
Sql: `select * from product_table where id = xxx`,
StatementName: 'statement-name',
WithEvent: true
};
redshiftdata.executeStatement(params,
async function(err, data){
if (err) console.log(err, err.stack);
else {
const resultParams : aws.RedshiftData.GetStatementResultRequest = { Id: data.Id! };
redshiftdata.getStatementResult(resultParams, function(err, data){
if (err) console.log(err, err.stack);
else console.dir(data, {depth: null});
})
}
});
Any suggestions or pointers would be really appreciated.
Thanks for the answer Parsifal. Here's a code snippet of the working solution.
import aws from "aws-sdk";
var roleToAssume = {RoleArn: 'arn:aws:iam::<accountid>:role/<rolename>',
RoleSessionName: 'example',
DurationSeconds: 900,};
var sts = new aws.STS({ region: '<region>'});
sts.assumeRole(roleToAssume, function(err, data) {
if (err)
{
console.log(err, err.stack);
}
else
{
aws.config.update({
accessKeyId: data.Credentials?.AccessKeyId,
secretAccessKey: data.Credentials?.SecretAccessKey,
sessionToken: data.Credentials?.SessionToken
})
// Redshift code here...
}
});

AWS Upload to S3 through Lambda

Trying to upload pictures to S3 from lambda. I get a return code of 200 when uploading the picture from my phone but the image is never uploaded to the bucket? Is it something to do with a bucket policy? The lambda function :
const AWS = require('aws-sdk');
AWS.config.update({
region: 'us-west-2'
})
const s3 = new AWS.S3();
exports.handler = async (event, context, callback) => {
AWS.config.update({region: 'us-west-2'});
// var buf = Buffer.from(event.picture.imageBinary.replace(/^data:image\/\w+;base64,/, ""),'base64')
let encodedImage =JSON.parse(event.picture);
let decodedImage = Buffer.from(encodedImage, 'base64');
var filePath = "avatars/" + event.userid + ".jpg"
var params = {
Body: decodedImage,
Bucket: 'testpictures-1',
Key: filePath,
ContentEncoding: 'base64',
ContentType: 'image/jpeg'
};
s3.putObject(params, function(err, data) {
if (err){ console.log(err, err.stack);} // an error occurred
else {console.log(data);} // successful response
});
};
Because you are trying to invoke the Amazon S3 Service from a Lambda function, you must ensure that the IAM role associated with the Lambda function has the correct S3 policies. If the IAM role does not have the policies related to an AWS Service , then you cannot successfully invoke the AWS Service from a Lambda function. Here is an AWS tutorial (implemented in Java) that discusses this point.
https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javav2/usecases/creating_scheduled_events

AWS DynamoDB: "Error: Missing region in config" - even with 'config.region' returning 'eu-west-1'

While following the AWS docs for setting up DynamoDB in my front end project, with settings taken from the docs the API throws:
Error: Missing region in config
at constructor.<anonymous> (aws-sdk-2.129.0.min.js:42)
at constructor.callListeners (aws-sdk-2.129.0.min.js:44)
at i (aws-sdk-2.129.0.min.js:44)
at aws-sdk-2.129.0.min.js:42
at t (aws-sdk-2.129.0.min.js:41)
at constructor.getCredentials (aws-sdk-2.129.0.min.js:41)
at constructor.<anonymous> (aws-sdk-2.129.0.min.js:42)
at constructor.callListeners (aws-sdk-2.129.0.min.js:44)
at constructor.emit (aws-sdk-2.129.0.min.js:44)
at constructor.emitEvent (aws-sdk-2.129.0.min.js:43)
My settings:
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.129.0.min.js"></script>
<script>
var myCredentials = new AWS.CognitoIdentityCredentials({IdentityPoolId:'eu-west-1_XXXXXX'});
var myConfig = new AWS.Config({
credentials: myCredentials, region: 'eu-west-1',
});
console.log(myConfig.region); //logs 'eu-west-1'
var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
dynamodb.listTables({Limit: 10}, function(err, data) {
if (err) {
console.log(err);
} else {
console.log("Table names are ", data.TableNames);
}
});
</script>
What am I missing?
Looks like you’re newing up AWS.config.
Change the line
var myConfig = new AWS.Config({
credentials: myCredentials, region: 'eu-west-1',
});
to
AWS.config.update({
credentials: myCredentials, region: 'eu-west-1',
}});
Reference:
http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-region.html
Hope it helps.
For others hitting the same issue, where the docs mention:
If you have not yet created one, create an identity pool...
and you got forwarded to the Amazon Cognito service, choose the Manage Federal Identities not the Manage User Pools option.

How to call AWS API Gateway Endpoint with Cognito Id (+configuration)?

I want to call an AWS API Gateway Endpoint that is protected with AWS_IAM using the generated JavaScript API SDK.
I have a Cognito UserPool and a Cognito Identity Pool. Both properly synced via ClientId.
I use this code to Sign in and get the Cognito Identity
AWS.config.region = 'us-east-1'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:XXXXXXXXXXXXXXXXXXXXXXXX' // your identity pool id here
});
AWSCognito.config.region = 'us-east-1';
AWSCognito.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:XXXXXXXXXXXXXXXXXXXXXXXX' // your identity pool id here
});
var poolData = {
UserPoolId: 'us-east-1_XXXXXXXX',
ClientId: 'XXXXXXXXXXXXXXXXXXXXXXXX'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var authenticationData = {
Username: 'user',
Password: '12345678',
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var userData = {
Username: 'user',
Pool: userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:XXXXXXXXXXXXXXXXXXXX',
IdentityId: AWS.config.credentials.identityId,
Logins: {
'cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXX': result.idToken.jwtToken
}
});
AWS.config.credentials.get(function (err) {
// now I'm using authenticated credentials
if(err)
{
console.log('error in autheticatig AWS'+err);
}
else
{
console.log(AWS.config.credentials.identityId);
}
});
},
onFailure: function (err) {
alert(err);
}
});
All this succeeds and I have an authorized Cognito Identity now.
Now I try to call the API Gateway Endpoint to execute the Lambda Function it points to.
var apigClient = apigClientFactory.newClient({
accessKey: AWS.config.credentials.accessKeyId, //'ACCESS_KEY',
secretKey: AWS.config.credentials.secretAccessKey, //'SECRET_KEY',
sessionToken: AWS.config.credentials.sessionToken, // 'SESSION_TOKEN', //OPTIONAL: If you are using temporary credentials you must include the session token
region: 'us-east-1' // OPTIONAL: The region where the API is deployed, by default this parameter is set to us-east-1
});
var params = {
// This is where any modeled request parameters should be added.
// The key is the parameter name, as it is defined in the API in API Gateway.
};
var body = {
// This is where you define the body of the request,
query: '{person {firstName lastName}}'
};
var additionalParams = {
// If there are any unmodeled query parameters or headers that must be
// sent with the request, add them here.
headers: {},
queryParams: {}
};
apigClient.graphqlPost(params, body, additionalParams)
.then(function (result) {
// Add success callback code here.
console.log(result);
}).catch(function (result) {
// Add error callback code here.
console.log(result);
});
But unfortunately this fails. The OPTIONS request succeeds with 200 but the POST then fails with 403.
I am pretty sure that there is no CORS problem here.
I am pretty sure the problem has to do with IAM Roles and AWS Resource Configurations.
My question is basically, can you please provide me with all the necessary AWS Resource Configurations and IAM Roles that are necessary for this to work please?
Resources I have are
API Gateway - with deployed API Endpoints
Lambda Function - called by the Endpoint
Cognito User Pool - with App synced to the Identity Pool
Cognito Identity Pool - with Authorized and Unauthorized Role mapped to it.
IAM Roles - for the Lambda Function and the Authorized and Unauthorized Role of the Cognito Identity Pool.
But I don't know how these Resources need to be configured properly to get this to work.
Thank you
What access permissions does the role of the Cognito Identity have? Make sure it has access to perform execute-api:Invoke on your API.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:us-east-1:<account>:<rest-api>/*/POST/graphql"
]
}
]
}
You can get the exact resource ARN from the method settings page in the web console.
Even after following everything I was getting the same error. And the reason was I missed the "sessionToken" while initialising the apigClient.
var apigClient = apigClientFactory.newClient({
accessKey: AWS.config.credentials.accessKeyId, //'ACCESS_KEY',
secretKey: AWS.config.credentials.secretAccessKey, //'SECRET_KEY',
sessionToken: AWS.config.credentials.sessionToken, // 'SESSION_TOKEN', //OPTIONAL: If you are using temporary credentials you must include the session token
region: 'us-east-1' // OPTIONAL: The region where the API is deployed, by default this parameter is set to us-east-1 });
//OPTIONAL: If you are using temporary credentials you must include the session token -- is not really optional

AWSCognito Missing region in config error

I'm using the aws-sdk javascript in my back-end and I can use AWS fine but when I try to use the getOpenIdTokenForDeveloperIdentity method I get a "Missing region in config error" as a response.
var config = new AWS.Config({
accessKeyId: "MYACCESSKEY", secretAccessKey: "MYSECRETYKEY", region: 'us-east-1'
});
var params = {
IdentityPoolId: 'MYIDENTITYPOOLID', /* required */
Logins: { /* required */
"login.my.myapp": 'string',
/* anotherKey: ... */
},
IdentityId: null,
TokenDuration: 0
};
cognitoidentity.getOpenIdTokenForDeveloperIdentity(params,function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
In the docs it said that:
By default, credentials and region settings are left unconfigured. This should be configured by the application before using any AWS service APIs.
So I set my region but why am I still getting an error?
You are setting the region in a local config variable. It should be set in the global AWS.config, like this:
AWS.config.region = 'us-east-1';
The same applies for the credentials. In case you want to use Amazon Cognito Credentials for all your AWS clients, you should initialize AWS.config.credentials like this:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'YOUR_POOL_ID'
});
I hope this helps!