Amazon SP-API can't assume role of myself? - node.js

I'm trying to connect to the Amazon Selling Partners API (SP-API) using the node.js library and am coming across an extremely odd error which seems to be telling me I can't assume my own role?
CustomError: User: arn:aws:iam::11111:user/bob is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::11111:user/bob
I'm fairly new to AWS but I'm pretty sure that this inline policy for the user should be sufficient for what I'm trying to do, I've even made it work for all resources rather than just the SellingPartners role I'd previously created:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "*"
}
]
}
Here's my full code in case it helps:
const SellingPartnerAPI = require('amazon-sp-api');
(async() => {
try {
let sellingPartner = new SellingPartnerAPI({
region:'na', // The region to use for the SP-API endpoints ("eu", "na" or "fe")
refresh_token:'xxxxxx', // The refresh token of your app user
credentials:{
SELLING_PARTNER_APP_CLIENT_ID:'xxxxx',
SELLING_PARTNER_APP_CLIENT_SECRET:'xxxxx',
AWS_ACCESS_KEY_ID:'xxxx',
AWS_SECRET_ACCESS_KEY:'xxxxx',
AWS_SELLING_PARTNER_ROLE:'arn:aws:iam::11111:user/bob'
}
});
let res = await sellingPartner.callAPI({
operation:'getOrders',
endpoint:'orders'
});
console.log(res);
} catch(e) {
console.log(e);
}
})();

The ARN arn:aws:iam::11111:user/bob describes a User not a role.
It should probably be something like arn:aws:iam::11111:role/your-role-name if the client expects a Role ARN.

Related

Access Denied to amazon AWS SQS through CLI or API

I am getting access denied when I try to access the Amazon SQS through CLI or API. This is the node.js code:
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({region: 'eu-west-1'});
var credentials = new AWS.SharedIniFileCredentials({profile: 'default'});
AWS.config.credentials = credentials;
// Create the SQS service object
var sqs = new AWS.SQS({apiVersion: 'latest'});
var params = {
MaxResults: 10
};
sqs.listQueues(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
The sqs policy is the following:
{
"Version": "2012-10-17",
"Id": "__default_policy_ID",
"Statement": [
{
"Sid": "__owner_statement",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::437568002678:root",
"arn:aws:iam::ACCOUNTID:role/MyRole",
"arn:aws:iam::ACCOUNTID:user/username"
]
},
"Action": [
"sqs:GetQueueAttributes",
"sqs:SendMessage",
"sqs:ListQueues"
],
"Resource": "arn:aws:sqs:eu-west-1:ACCOUNTID:NAME"
}
]
}
The complete error is:
Error AccessDenied: Access to the resource https://sqs.eu-west-1.amazonaws.com/ is denied.
The credentials file is set at /home/user/.aws/credentials.
I am using one account for AWS services.
To reproduce your situation I did the following:
Created an IAM User with no permissions
Configured the AWS CLI to use credentials associated with the new IAM User
Created a standard (non-FIFO) Amazon SQS queue
Attached your policy to the queue, but with an updated Resource and Principal
Used the AWS CLI to send a message to the SQS queue:
aws sqs send-message --queue-url https://sqs.ap-southeast-2.amazonaws.com/123456789012/queuename --message-body foo
The message was successfully sent to the queue. This means that the Access Policy on the SQS queue was sufficient to grant permission to send the message, without requiring any additional permissions on the IAM User.
Therefore, either your AWS CLI is not using the IAM User referenced in your Access Policy or there are some permissions on the IAM User or Account SCP that is specifically denying the ability to use SendMessage().

IAM permission issue when using Cognito UnAuth role

I'm making a simple React app to access RDS data via DescribeDBInstances API. I want to allow public access, so I'm using Cognito with Unauthenticated access enabled.
I have the following policy attached to the provided UnAuth role, yet I'm still getting the following error when trying to use the RDS API from JavaScript (nodejs):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "rds:DescribeDBInstances",
"Resource": "*"
}
]
}
AccessDenied: User: arn:aws:sts::(account):assumed-role/Cognito_TestUnauth_Role/CognitoIdentityCredentials is not authorized to perform: rds:DescribeDBInstances on resource: arn:aws:rds:us-east-1:(account):db:*
I redacted my account ID.
Also this default policy is attached:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"mobileanalytics:PutEvents",
"cognito-sync:*"
],
"Resource": "*"
}
]
}
Here's my calling code:
import { RDSClient, DescribeDBInstancesCommand } from "#aws-sdk/client-rds";
import { CognitoIdentityClient } from "#aws-sdk/client-cognito-identity";
import { fromCognitoIdentityPool } from "#aws-sdk/credential-provider-cognito-identity";
// see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rds/index.html
export default async function getDbInstances() {
const region = "us-east-1";
const client = new RDSClient({
region,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region }),
identityPoolId: "(my identity pool ID)",
})
});
const command = new DescribeDBInstancesCommand({});
return await client.send(command).DBInstances;
}
I'm going a bit crazy here, it seems everything is set up correctly. What is missing?
I found the answer inside the IAM Roles documentation for Cognito: https://docs.aws.amazon.com/cognito/latest/developerguide/iam-roles.html (see "Access Policies" section)
Enhanced authentication is recommended for Cognito and enabled by default, but since it uses the GetCredentialForIdentity API under the hood, access is limited to certain AWS services regardless of IAM policy (RDS isn't an allowed service). I didn't see any way to override this limitation.
The solution is to switch to basic authentication (you have to enable it first in the Cognito identity pool settings). Here's my working nodejs code to use basic auth and then fetch the RDS instances:
import { RDSClient, DescribeDBInstancesCommand } from "#aws-sdk/client-rds";
import {
CognitoIdentityClient,
GetIdCommand ,
GetOpenIdTokenCommand
} from "#aws-sdk/client-cognito-identity";
import { getDefaultRoleAssumerWithWebIdentity } from "#aws-sdk/client-sts";
import { fromWebToken } from "#aws-sdk/credential-provider-web-identity";
const region = "us-east-1";
const cognitoClient = new CognitoIdentityClient({ region })
// see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rds/index.html
export default async function getDbInstances() {
const { Token, IdentityId } = await getTokenUsingBasicFlow();
const client = new RDSClient({
region,
credentials: fromWebToken({
roleArn: "arn:aws:iam::(account id):role/Cognito_RDSDataAppPoolUnauth_Role",
webIdentityToken: Token,
roleSessionName: IdentityId.substring(IdentityId.indexOf(":") + 1),
roleAssumerWithWebIdentity: getDefaultRoleAssumerWithWebIdentity()
})
});
const command = new DescribeDBInstancesCommand({});
return (await client.send(command)).DBInstances;
}
async function getTokenUsingBasicFlow() {
const getIdCommand = new GetIdCommand({ IdentityPoolId: "us-east-1:(identity pool id)" });
const id = (await cognitoClient.send(getIdCommand)).IdentityId;
const getOpenIdTokenCommand = new GetOpenIdTokenCommand({ IdentityId: id });
return await cognitoClient.send(getOpenIdTokenCommand);
}
Here's the documentation for the basic authentication flow vs enhanced that I followed to write my implementation: https://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html

How to get url from s3.getSignedUrl()

I'm trying to store some images using AWS S3. Everything is running smoothly until I started getting some 400s on PUTting images on URLs I got from s3.getSignedUrl. At that time my code looked like this:
const s3 = new AWS.S3({
accessKeyId,
secretAccessKey
});
const imageRouter = express.Router();
imageRouter.post('/upload', (req, res) => {
const type = req.body.ContentType;
const Key = `${req.session.user.id}/${uuid()}.${type}`;
s3.getSignedUrl(
'putObject',
{
Bucket: 'cms-bucket-06',
ContentType: type,
Key
},
(err, url) => {
console.log('URL ', url); res.send({ Key, url });
}
);
});
I followed link from error and I found out that "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.".
So I did. Like that:
const s3 = new AWS.S3({signatureVersion: 'v4'});
But now I get no URL in my callback function. It's undefined. What am I still missing here?
EDIT:
Alright, I added my key back to the constructor and I'm able to upload images. New problem is that I can't open them. I get access denied every time. I added proper bucket policy but it doesn't help :(
{
"Version": "2012-10-17",
"Id": "Policy1547050603038",
"Statement": [
{
"Sid": "Stmt1547050601490",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket-name/*"
}
]
}

Access denied when making api calls to s3 bucket with Node.js

Using Node.js, I'm making an api that makes calls to my s3 bucket on AWS. When I try to make use putObject method, i receive this error:
message: 'Access Denied',
code: 'AccessDenied',
region: null,
time: 2018-07-27T17:08:29.555Z,
... etc
}
I have a config and credentials file in C:/User/{User}/.aws/ directory
config file:
[default]
region=us-east-2
output=json
credentials file:
[default]
aws_access_key_id=xxxxxxxxxxxxxxx
aws_secret_access_key=xxxxxxxxxxx
I created policies for both IAM user and Bucket. Here's my IAM user inline policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets",
"s3:PutObject",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::*"
]
}
]
}
And my bucket policy:
{
"Version": "2012-10-17",
"Id": "Policy1488494182833",
"Statement": [
{
"Sid": "Stmt1488493308547",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::134100338998:user/Test-User"
},
"Action": [
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:GetBucketLocation",
"s3:Get*",
"s3:Put*"
],
"Resource": "arn:aws:s3:::admin-blog-assets"
}
]
}
And finally, my api
var fs = require('fs'),
AWS = require('aws-sdk'),
s3 = new AWS.S3('admin-blog-assets');
...
var params = {
Bucket: 'admin-blog-assets',
Key: file.filename,
Body: fileData,
ACL:'public-read'
};
s3.putObject(params, function (perr, pres) {
if (perr) {
console.log("Error uploading image: ", perr);
} else {
console.log("uploading image successfully");
}
});
I've been banging my head on this for hours, can anyone help?
I believe the source of the problem is related to how you are defining the s3 object, as s3 = new AWS.S3('admin-blog-assets');
If you look at the example used here, it has this line:
var bucketPromise = new AWS.S3({apiVersion: '2006-03-01'}).createBucket({Bucket: bucketName}).promise();
Where the argument passed to AWS.S3 is an object containing that apiVersion field. But you are passing a string value.
The S3 specific documentation overview section has more information:
Sending a Request Using S3 var s3 = new AWS.S3();
s3.abortMultipartUpload(params, function (err, data) { if (err)
console.log(err, err.stack); // an error occurred else
console.log(data); // successful response }); Locking the
API Version In order to ensure that the S3 object uses this specific
API, you can construct the object by passing the apiVersion option to
the constructor:
var s3 = new AWS.S3({apiVersion: '2006-03-01'}); You can also set the
API version globally in AWS.config.apiVersions using the s3 service
identifier:
AWS.config.apiVersions = { s3: '2006-03-01', // other service API
versions };
var s3 = new AWS.S3();
Some of the permissions you were granting were bucket permissions and others were object permissions. There are actions matching s3:Get* and s3:Put* that apply to both buckets and objects.
"Resource": "arn:aws:s3:::example-bucket" is only the bucket itself, not the objects inside it.
"Resource": "arn:aws:s3:::example-bucket/*" is only the objects in the bucket, and not the bucket itself.
You can write two policy statements, or you can combine the resources, like this:
"Resource": [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket/*"
]
Important Security Consideration: By using s3:Put* with both the bucket and object ARNs, your policy likely violates the principle of least privilege, because you have implicitly granted this user s3:PutBucketPolicy which allows these credentials to change the bucket policy. There may be other, similar concerns. You probably do not want to give these credentials that much control.
Credit to #PatNeedham for noticing a second issue that I overlooked, the AWS.S3() constructor expects an object as its first argument, not a string.

Receiving Access Denied when trying to access Amazon AWS S3 service with nodejs

var AWS = require('aws-sdk');
var S3FS = require('s3fs');
AWS.config.update({region: 'us-east-1'});
var options = {};
exports.storeMedia = function(req, res){
var fsImpl = new S3FS('test-bucket', options);
fsImpl.writeFile('message.txt', 'Hello Node', function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
}
My code is just a sample code I am trying to test out of the doc samples. I have saved the aws key id and secret in the credentials file under .aws on my macbook. I have already built a user and assigned it access policies such that I am able to get and put objects through the assigned user on AWS Console. What else should I look at? Obviously, I am missing something basic just need a little help over the hump.
I researched a couple of additional things. One, I looked at the module page from https://www.npmjs.com/package/s3fs which lists the minimum privileges that s3fs requires.
Secondly, I tried to use the base aws-sdk. I was surprised to see that I could do a ListBucket without any issues. Then, I tried to put an object in an existing bucket, and that worked without issues. I am able to make progress with that, but I need to understand more about which privilege I may not have given so I clearly understand which privilege is missing.
my policy for the bucket is as follows:
{
"Version": "2012-10-17",
"Id": "Policy1437933054275",
"Statement": [
{
"Sid": "Stmt1437932987273",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::meetsites-images/*",
"arn:aws:s3:::meetsites-images"
]
}
]
}
and the policy for the user is
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
I was also able to find a good blog on this topic, hard to find so better to mention [a link] (http://blogs.aws.amazon.com/security/post/TxPOJBY6FE360K/IAM-policies-and-Bucket-Policies-and-ACLs-Oh-My-Controlling-Access-to-S3-Resourc)

Resources