Error of authorized to access this resource on postman - postman

I'm using postman for an api request. My question is; I'm getting an error when I made one of the variables random.
For example when I sent request this link;
https://testrest.xxxxxx.com/voucher/purchase/testAUD000/49/1/30000017/1 it is working. But when I change with this;
https://testrest.xxxxxx.com/voucher/purchase/testAUD000/49/1/30000017/{{transactionId}}
I'm getting this error; "You are not authorized to access this resource"
In my collection pre-request script;
var apiKey = pm.collectionVariables.get("API-KEY");
var apiSecret = pm.collectionVariables.get("API-SECRET");
var nonce = _.random(1, 50000000);
var signature = pm.request.method + "\n" + pm.request.url.getPath() + "\n" + nonce + "\n";
var hash = CryptoJS.HmacSHA256(signature, apiSecret).toString();
pm.request.headers.add("AUTHENTICATION: HMAC " + apiKey + ":" + hash + ":" + nonce);
I added this in collection pre-request script or request in pre-request script I'm getting error;
var random = Math.floor(Math.random() * 10);
pm.variables.set('transactionId',random)

Related

React-Native - Fetch image from AWS S3 Bucket via HTTP request

I am trying to fetch a jpg image from AWS S3 Bucket using a HTTP GET request in React-Native.
So far I was following these 2 documentations from Amazon :
https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
Here is the logic I implemented so far.
When my component render, I am doing the following :
import { Auth } from 'aws-amplify';
import Moment from 'moment';
import sha256 from 'crypto-js/sha256';
var CryptoJS = require("crypto-js");
...
Auth.currentCredentials()
.then(async credentials => {
let awsHost = 'https://myAwsHost.amazonaws.com'
let awsPath = '/public/pathToMyImage.jpg'
let accessKeyId = credentials.accessKeyId
let secretAccessKey = credentials.secretAccessKey
let sessionToken = credentials.sessionToken
let awsDate = Moment().utc().format("YYYYMMDD")
let awsRequestDateTime = Moment().utc().format("YYYYMMDD" + "T" + "HHmmss") + "Z"
let awsRegion = "myRegion"
let awsService = "s3"
let awsRequest = "aws4_request"
let awsAlgorithm = "AWS4-HMAC-SHA256"
let awsCredentialScope = awsDate + "/" + awsRegion + "/" + awsService + "/" + awsRequest
let awsHTTPRequestMethod = "GET"
let awsSignedHeaders = "host;x-amz-content-sha256;x-amz-date;x-amz-security-token"
let awsCanonicalURI = awsHost + awsPath
let awsCanonicalQueryString = ""
// Step 1
let canonicalRequest =
awsHTTPRequestMethod + '\n' +
awsCanonicalURI + '\n' +
awsCanonicalQueryString + '\n' +
"X-Amz-Content-Sha256".toLowerCase() + ":" + sha256("") + "\n" +
"X-Amz-Date".toLowerCase() + ":" + awsRequestDateTime.trim() + "\n" +
"X-Amz-Security-Token".toLowerCase() + ":" + sessionToken + "\n" +
awsSignedHeaders + '\n' +
sha256("")
// Step 2 :
let stringToSign =
awsAlgorithm + "\n" +
awsRequestDateTime + "\n" +
awsCredentialScope + "\n" +
sha256(canonicalRequest)
// Step 3 :
let kSecret = secretAccessKey
let kDate = CryptoJS.HmacSHA256("AWS4" + kSecret, awsDate)
let kRegion = CryptoJS.HmacSHA256(kDate, awsRegion)
let kService = CryptoJS.HmacSHA256(kRegion, awsService)
let kSigning = CryptoJS.HmacSHA256(kService, awsRequest).toString(CryptoJS.enc.Hex)
let awsSignature = CryptoJS.HmacSHA256(awsDerivedSignInKey, stringToSign).toString(CryptoJS.enc.Hex)
let awsAuthorization = awsAlgorithm + " Credential=" + accessKeyId + "/" + awsCredentialScope + ", SignedHeaders=" + awsSignedHeaders + ", Signature=" + awsSignature
// Fetching the image with an HTTP GET request to AWS S3 Buckets
try {
await fetch(
awsCanonicalURI,
{
headers: {
'X-Amz-Security-Token': sessionToken,
'X-Amz-Content-Sha256': sha256("").toString(),
'X-Amz-Date': awsRequestDateTime,
'Authorization': awsAuthorization
},
}
).then(res => {
console.log("HTML status : " + res.status) // Status return : Error 403
});
} catch (error) {
console.error(error);
}
})
}
When I am trying to execute the same GET request with postman it work's and it retrieve the image from AWS S3 Buckets.
The only difference between my request and the one generated by postman is the Signature.
I know that I can fetch images from S3Image component from aws-amplify-react-native but it is not what I am trying to achieve.
Main goal
Finally, I am looking to execute those HTTP GET Request's in order to use them in FastImage from the module react-native-fast-image and use it in order to cache easely images in my react-native application.
If anyone has an answer to my problem or a better alternative to what I am trying to achieve, be my guest!!
If it works in postman, You can get the Axios code sample for that using postman. Checkout the gif below.

Can't replicate simple hashing signature example from Amazon

I'm trying to create the final step of this example in Flutter and I can't get it right for some reason:
https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
Using their Signing Key + String to sign they get this resulting signature:
5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
Using the exact same key / string to sign I end up with:
fe52b221b5173b501c9863cec59554224072ca34c1c827ec5fb8a257f97637b1
Here is my code:
var testSigninKey =
'c4afb1cc5771d871763a393e44b703571b55cc28424d1a5e86da6ed3c154a4b9';
var testStringToSign = 'AWS4-HMAC-SHA256' + '\n' +
'20150830T123600Z' + '\n' +
'20150830/us-east-1/iam/aws4_request' + '\n' +
'f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59';
var hmac1 = Hmac(sha256, utf8.encode(testSigninKey));
var digest1 = hmac1.convert(utf8.encode(testStringToSign));
print(digest1);
print(hex.encode(digestBytes.bytes));
//both print: fe52b221b5173b501c9863cec59554224072ca34c1c827ec5fb8a257f97637b1
The hmac1 calls expects you to use the signing key as is, not to use an encoded version of the hex string representation of it.
You should be able to properly construct the Hmac object with this:
var hmac1 = Hmac(sha256, hex.decode(testSigninKey));
Here is a complete example of my flutter code, referring to the s3 signature creation example:
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
I know it's not perfect but it took me extremely long to figure it out. Maybe it helps someone.
import 'dart:convert';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:http/http.dart' as http;
class AWS {
var _algorithm = ["X-Amz-Algorithm", "AWS4-HMAC-SHA256"];
final String _awsService = "s3";
final String _aws4Request = "aws4_request";
var _expires = ["X-Amz-Expires", "86400"];
var _signedHeaders = ["X-Amz-SignedHeaders", "host"];
var testbucket = "https://s3.amazonaws.com/examplebucket";
var testfilePath = "/test.txt";
var _testDate = ["X-Amz-Date", "20130524T000000Z"];
var _testDateymd = 20130524;
var _testRegion = "us-east-1";
var _testCredential = [
"X-Amz-Credential",
"AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request"
];
var testSecretkey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
testString() {
var httpMethod = "GET";
var canUri = testfilePath;
var canQueryStr = Uri.encodeQueryComponent(_algorithm[0]) +
"=" +
Uri.encodeQueryComponent(_algorithm[1]) +
"&" +
Uri.encodeQueryComponent(_testCredential[0]) +
"=" +
Uri.encodeQueryComponent(_testCredential[1]) +
"&" +
Uri.encodeQueryComponent(_testDate[0]) +
"=" +
Uri.encodeQueryComponent(_testDate[1]) +
"&" +
Uri.encodeQueryComponent(_expires[0]) +
"=" +
Uri.encodeQueryComponent(_expires[1]) +
"&" +
Uri.encodeQueryComponent(_signedHeaders[0]) +
"=" +
Uri.encodeQueryComponent(_signedHeaders[1]);
// <CanonicalQueryString>\n
// <CanonicalHeaders>\n
// <SignedHeaders>\n
// <HashedPayload>
var _canonicalRequest = httpMethod +
"\n" +
canUri +
"\n" +
canQueryStr +
"\n" +
"host:examplebucket.s3.amazonaws.com" +
"\n" +
"\n" +
"host" +
"\n" +
"UNSIGNED-PAYLOAD";
var bytes = utf8.encode(_canonicalRequest);
var _stringToSign = "AWS4-HMAC-SHA256" +
"\n" +
"20130524T000000Z" +
"\n" +
"20130524/us-east-1/s3/aws4_request" +
"\n" +
sha256.convert(bytes).toString();
print("String to sign: $_stringToSign");
// HEX und HMAC signature
List<int> _dateKey = utf8.encode("AWS4" + testSecretkey);
List<int> _dateMsg = utf8.encode(_testDateymd.toString());
Hmac dateHmac = Hmac(sha256, _dateKey);
Digest dateDigest = dateHmac.convert(_dateMsg);
String _dateRegionKey = dateDigest.toString();
List<int> _dateRegionMsg = utf8.encode(_testRegion.toString());
Hmac dateRegionHmac = Hmac(sha256, hex.decode(_dateRegionKey.toString()));
Digest dateRegionDigest = dateRegionHmac.convert(_dateRegionMsg);
String _dateRegionServiceKey = dateRegionDigest.toString();
List<int> _dateRegionServiceMsg = utf8.encode(_awsService.toString());
Hmac dateRegionServiceHmac =
Hmac(sha256, hex.decode(_dateRegionServiceKey.toString()));
Digest dateRegionServiceDigest =
dateRegionServiceHmac.convert(_dateRegionServiceMsg);
String _signingKey = dateRegionServiceDigest.toString();
List<int> _signingKeyMsg = utf8.encode(_aws4Request.toString());
Hmac _signingKeyHmac = Hmac(sha256, hex.decode(_signingKey.toString()));
Digest _signingKeyDigest = _signingKeyHmac.convert(_signingKeyMsg);
print("signing key: $_signingKeyDigest");
var _signatureKey = _signingKeyDigest.toString();
List<int> _signatureKeyMsg = utf8.encode(_stringToSign);
Hmac _signatureHmac = Hmac(sha256, hex.decode(_signatureKey));
var _signature = _signatureHmac.convert(_signatureKeyMsg);
print("Signature: $_signature");
}
}

Generating signatures for AWS S3 in Google Scripts

I'm trying to generate the signature in Google Scripts without much luck.
My code is as follows:
function getAwsData(){
var AWS_SECRET = '[AWS_SECRET]';
var expiresDt = Math.floor(Date.now() / 1000) + (60 * 60 * 24);
var path = '/MYFOLDER/ACTIVATION.csv'
var stringToSign = 'GET\n\n\n' + expiresDt + '\n' + path;
var hmac = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_1, AWS_SECRET,stringToSign, Utilities.Charset.UTF_8);
var signature = encodeURIComponent(Utilities.base64Encode(hmac));
var response = UrlFetchApp.fetch("http://s3-ap-southeast-2.amazonaws.com/MYFOLDER/ACTIVATION.csv?AWSAccessKeyId=[AWS_ACCESS_ID]&Expires="+expiresDt+"&Signature="+signature);
var csvData = Utilities.parseCsv(response, ",");
console.log(csvData);
}
Can someone please see where I've gone wrong? I've followed the document here: http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html
It appears that I'm signing the hmac incorrectly.
var hmac = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_1, stringToSign, AWS_SECRET,Utilities.Charset.UTF_8);

Accessing AWS API Gateway from an EC2 using IAM authorization (NodeJS)

Perhaps I'm going a bridge to far here but heres what I got:
An AWS API Gateway Method that has AWS_IAM set for Authorization.
A Policy that allows access to that Method.
An EC2 Role that has that policy attached to it.
An EC2 Launched with that Role.
I would like to have my NodeJS program (or any language for that matter) on that EC2 to be able to call that API without hardcoding an AccessKey and SecretKey in the code.
I have used this approach to use the aws-sdk to put/get records on S3, and do other AWS functionality (like all the steps I mentioned above), However, invoking an API Gateway seems to be outside the aws-sdk scope.
Calling the API with Wreck (the NPM I use from my HTTP calls in my app) and no headers results in:
{
"message": "Missing Authentication Token"
}
Not a big shock there.
Anything obvious I am missing?
So it appears you need to access your EC2 at http://169.254.169.254/latest/meta-data/iam/security-credentials/Role_Name
As explained here.
Here is my final code including Signing AWS Requests with Signature Version 4:
var Moment = require('moment');
var Wreck = require('wreck');
var Crypto = require('crypto');
function getSignatureKey(key, dateStamp, regionName, serviceName) {
var kDate = Crypto.createHmac('sha256', 'AWS4' + key).update(dateStamp,'utf8').digest();
var kRegion = Crypto.createHmac('sha256', kDate).update(regionName, 'utf8').digest();
var kService = Crypto.createHmac('sha256', kRegion).update(serviceName, 'utf8').digest();
var kSigning = Crypto.createHmac('sha256', kService).update('aws4_request', 'utf8').digest();
return kSigning;
}
var assumed_role = 'MY_ROLE';
Wreck.get('http://169.254.169.254/latest/meta-data/iam/security-credentials/' + assumed_role, function(err, res, payload) {
var payload_obj = JSON.parse(payload.toString());
var access_key = payload_obj.AccessKeyId;
var secret_key = payload_obj.SecretAccessKey;
var token = payload_obj.Token;
var payload = {}
payload.email = 'devin.stewart#example.com';
payload.first_name = 'Devin';
payload.last_name = 'Stewart';
payload.full_name = 'Devin Stewart';
var request_parameters = JSON.stringify(payload);
var method = 'POST';
var api_id = 'MY_API_ID'
var service = 'execute-api';
var region = 'us-east-1';
var api_path = '/production/people';
var host = api_id + '.' + service + '.' + region + '.amazonaws.com';
var endpoint = 'https://' + host + api_path;
var content_type = 'application/json';
var t = Moment.utc()
var amz_date = t.format('YYYYMMDD[T]HHmmss[Z]');
var date_stamp = t.format('YYYYMMDD'); // Date w/o time, used in credential scope
var canonical_querystring = '';
var canonical_headers = 'content-type:' + content_type + '\n' + 'host:' + host + '\n' + 'x-amz-date:' + amz_date + '\n' + 'x-amz-security-token:' + token + '\n';
var signed_headers = 'content-type;host;x-amz-date;x-amz-security-token';
var payload_hash = Crypto.createHash('sha256').update(request_parameters).digest('hex');
var canonical_request = method + '\n' + api_path + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash;
var algorithm = 'AWS4-HMAC-SHA256';
var credential_scope = date_stamp + '/' + region + '/' + service + '/' + 'aws4_request';
var string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + Crypto.createHash('sha256').update(canonical_request).digest('hex');
var signing_key = getSignatureKey(secret_key, date_stamp, region, service);
var signature = Crypto.createHmac('sha256', signing_key).update(string_to_sign, 'utf8').digest('hex');
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature;
var headers = {
'Content-Type':content_type,
'X-Amz-Date':amz_date,
'X-Amz-Security-Token':token,
'Authorization':authorization_header
};
var options = {headers: headers, payload: request_parameters};
Wreck.post(endpoint, options, function (err, res, payload) {
if (err) {
console.log(err.data.payload.toString());
} else {
console.log(payload.toString());
}
});
});

How do you pass Authorization header through API Gateway to HTTP endpoint?

I have an API behind an AWS API Gateway that needs to use the Authorization header for processing. I have unfortunately been unable to pass this to the backend for processing.
I have tried creating the Authorization HTTP Request Header in my Method Request and then creating the corresponding Authorization HTTP Header in my Integration Request (Authorization is mapped from method.request.header.Authorization in this case). I log all of the headers that the backend receives, and from the log, I can see other headers that I have listed in the Integration Request but not Authorization.
I have also tried creating a mapping template with Content-Type application/json and the template defined as
{
"AccountID": "$context.identity.accountId",
"Caller": "$context.identity.caller",
"User": "$context.identity.user",
"Authorization": "$input.params().header.get('Authorization')",
"UserARN": "$context.identity.userArn"
}
Yet, the backend logs show that there is still no Authorization header nor any Authorization field in the JSON body. I also cannot see the user's ARN. I have seen other examples and threads where users have mentioned accessing the Authorization field on the event object that is passed into a Lambda function, but I am not using a Lambda function.
I have made sure to Deploy the API Gateway in both scenarios.
Does anyone know if there is some way I can pass the Authorization header through the API Gateway to my HTTP endpoint? Is there an alternative way to access the API caller's user name or ID?
Edit - Here's a snippet of the code I'm using to hit the API Gateway:
String awsAccessKey = "myaccesskey";
String awsSecretKey = "mysecretkey";
URL endpointUrl;
try {
endpointUrl = new URL("https://<host>/<path>/<to>/<resource>?startDate=20151201&endDate=20151231");
} catch(Exception e) {
throw new RuntimeException("Unable to parse service endpoint: " + e.getMessage());
}
Date now = new Date();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
sdf1.setTimeZone(new SimpleTimeZone(0, "UTC"));
String dateTS = sdf1.format(now);
String headerNames = "host;x-amz-date";
String queryParameters = "endDate=20151231&startDate=20151201";
String canonicalRequest = "GET\n" +
"/<path>/<to>/<resource>\n" +
queryParameters + "\n" +
"host:<host>\n" +
"x-amz-date:" + dateTS + "\n" +
"\n" +
headerNames + "\n" +
"<sha256 hash for empty request body>";
System.out.println(canonicalRequest);
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd");
sdf2.setTimeZone(new SimpleTimeZone(0, "UTC"));
String dateStr = sdf2.format(now);
String scope = dateStr + "/us-east-1/execute-api/aws4_request";
String stringToSign =
"AWS4-HMAC-SHA256\n" +
dateTS + "\n" +
scope + "\n" +
"hex encoded hash of canonicalRequest";
System.out.println(stringToSign);
byte[] kSecret = ("AWS4" + awsSecretKey).getBytes();
byte[] kDate = HmacSHA256(dateStr, kSecret);
byte[] kRegion = HmacSHA256("us-east-1", kDate);
byte[] kService = HmacSHA256("execute-api", kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
byte[] signature = HmacSHA256(stringToSign, kSigning);
String credentialsAuthorizationHeader = "Credential=" + awsAccessKey + "/" + scope;
String signedHeadersAuthorizationHeader = "SignedHeaders=" + headerNames;
String signatureAuthorizationHeader = "Signature=" + "hex encoded signature";
String authorization = "AWS4-HMAC-SHA256 "
+ credentialsAuthorizationHeader + ", "
+ signedHeadersAuthorizationHeader + ", "
+ signatureAuthorizationHeader;
Map<String, String> headers = new HashMap<String, String>();
headers.put("x-amz-date", dateTS);
headers.put("Host", endpointUrl.getHost());
headers.put("Authorization", authorization);
headers.put("Content-Type", "application/json");
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) endpointUrl.openConnection();
connection.setRequestMethod("GET");
for (String headerKey : headers.keySet()) {
connection.setRequestProperty(headerKey, headers.get(headerKey));
}
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream is;
try {
is = connection.getInputStream();
} catch (IOException e) {
is = connection.getErrorStream();
}
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
StringBuffer response = new StringBuffer();
while ((line = rd.readLine()) != null) {
response.append(line);
response.append('\r');
}
rd.close();
System.out.println(response.toString());
} catch (Exception e) {
throw new RuntimeException("Error: " + e.getMessage(), e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
This is good enough to authenticate successfully and hit the HTTP endpoint on the backend.
As noted in comments, the Authorization header includes incomplete information for you to establish who the user is, so I wouldn't recommend going this route. Additionally, if AWS_IAM auth is enabled, the Authorization header will be consumed by API Gateway.
If AWS_IAM auth is enabled and the signature is supplied correctly, the $context.identity parameters should reflect the credentials used to sign the request.
If you use the test invoke feature in the console, do you see the context fields being filled in?
Update:
I'm unable to reproduce this issue.
I have an API with the following mapping template:
#set($path = $input.params().path)
#set($qs = $input.params().querystring)
{
"resource-path": "$context.resourcePath",
"http-method": "$context.httpMethod",
"identity": {
#foreach($key in $context.identity.keySet())
"$key": "$context.identity.get($key)"
#if($foreach.hasNext), #end
#end
},
"params": {
#foreach($key in $path.keySet())
"$key": "$path.get($key)"
#if($foreach.hasNext), #end
#end
},
"query": {
#foreach($key in $qs.keySet())
"$key": "$qs.get($key)"
#if($foreach.hasNext), #end
#end
},
"body": $input.json('$')
}
And a lambda function that simply spits back the input as output. When I sign the request and invoke the API, I get back the expected results:
{
"resource-path":"/iam",
"http-method":"GET",
"identity":{
"cognitoIdentityPoolId":"",
"accountId":"xxxxxxxx",
"cognitoIdentityId":"",
"caller":"AIDXXXXXXXXXXX,
"apiKey":"",
"sourceIp":"54.xx.xx.xx",
"cognitoAuthenticationType":"",
"cognitoAuthenticationProvider":"",
"userArn":"arn:aws:iam::xxxxxxxx:user/hackathon",
"userAgent":"Java/1.8.0_31",
"user":"AIDXXXXXXXXXXXXXX"
},
"params":{},
"query":{},
"body":{}
}
Currently the Authorization header can only be forwarded for methods that do not require AWS authentication. The SigV4 signing process relies on the Authorization header and we do not expose this for security purposes. If you have data you need to send (besides the SigV4 signature), you would need to send in another header.
In AWS API Gateway, Request Body is not supported for GET methods.
In Integration Request convert your GET to POST by specifying POST as your HTTP method. Then proceed with specifying the Body Mapping Template as proposed by #BobKinney
This way the request body will propagate properly, but the client will still be making a GET request as expected