HTTP response code: 403 - postmates

Using Java, making a simple "delivery_quotes" call for testing which return HTTP response code - 403
Below is the code:
String api_key = Base64.getEncoder().encodeToString("95xxxxx1-xxxx-44h1-8961-904xxx024fb:".getBytes("utf-8"));
String postData = "pickup_address=" + java.net.URLEncoder.encode("20 McAllister St, San Francisco, CA", "UTF-8") + "&" +
"dropoff_address=" + java.net.URLEncoder.encode("101 Market St, San Francisco, CA", "UTF-8");
String apiURL = "https://api.postmates.com/v1/customers/cus_LxxxxRaHNT_yqV/delivery_quotes";
URL myurl = new URL(apiURL);
HttpsURLConnection con = (HttpsURLConnection)myurl.openConnection();
con.setDoOutput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
con.setRequestProperty("Authorization", "Basic " + api_key);
OutputStream os = con.getOutputStream();
os.write(postData.getBytes());
InputStream istream = con.getInputStream();
int ch;
while((ch=istream.read()) != -1)
{
System.out.print((char)ch);
}
Exception in thread "main" java.io.IOException: Server returned HTTP response code: 403 for URL: https://api.postmates.com/v1/customers/cus_LBxxxxxxNT_yqV/delivery_quotes
Customer ID and Test API keys which I am using are correct. Any HELP!

This is what worked for me:
Put the customer id in the url, as you have done correctly.
Put the Sandbox into the username field.
Then set the Secret into the password field (on the right side of the :colon).

Related

How to google oauth to an api? My example is not working

I am trying to do this article for google cloud build
https://cloud.google.com/endpoints/docs/openapi/service-account-authentication
I am guessing to use the service account email I generated the key from in that example AND for Audient, I put "" (which is probably the reason it's not working?). I have no idea and can't find what in the world to put for audience.
In addition to code below, I tried setting audience to 'https://cloudbuild.googleapis.com' which also did not work
My code is the following...
public class GenToken {
public static void main(String[] args) throws IOException {
Duration d = Duration.ofDays(365);
String tok = generateJwt("/Users/dean/workspace/order/java/googleBuild/orderly-gcp-key.json",
"mycloudbuilder#order-gcp.iam.gserviceaccount.com", "", d.toSeconds());
System.out.println("tok="+tok);
URL url = new URL("https://cloudbuild.googleapis.com/v1/projects/order-gcp/builds");
makeJwtRequest(tok, "GET", url);
}
public static String generateJwt(final String saKeyfile, final String saEmail,
final String audience, final long expiryLength)
throws FileNotFoundException, IOException {
Date now = new Date();
Date expTime = new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiryLength));
// Build the JWT payload
JWTCreator.Builder token = JWT.create()
.withIssuedAt(now)
// Expires after 'expiraryLength' seconds
.withExpiresAt(expTime)
// Must match 'issuer' in the security configuration in your
// swagger spec (e.g. service account email)
.withIssuer(saEmail)
// Must be either your Endpoints service name, or match the value
// specified as the 'x-google-audience' in the OpenAPI document
.withAudience(audience)
// Subject and email should match the service account's email
.withSubject(saEmail)
.withClaim("email", saEmail);
// Sign the JWT with a service account
FileInputStream stream = new FileInputStream(saKeyfile);
ServiceAccountCredentials cred = ServiceAccountCredentials.fromStream(stream);
RSAPrivateKey key = (RSAPrivateKey) cred.getPrivateKey();
Algorithm algorithm = Algorithm.RSA256(null, key);
return token.sign(algorithm);
}
/**
* Makes an authorized request to the endpoint.
*/
public static String makeJwtRequest(final String signedJwt, String method, final URL url)
throws IOException, ProtocolException {
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod(method);
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Authorization", "Bearer " + signedJwt);
InputStreamReader reader = new InputStreamReader(con.getInputStream());
BufferedReader buffReader = new BufferedReader(reader);
String line;
StringBuilder result = new StringBuilder();
while ((line = buffReader.readLine()) != null) {
result.append(line);
}
buffReader.close();
return result.toString();
}
}
The orderly-gcp-key.json has these attributes in it
{
"type": "service_account",
"project_id": "myproj",
"private_key_id": "xxxxxxxx",
"private_key": "-----BEGIN PRIVATE KEY-----\nasdfsd\n-----END PRIVATE KEY-----\n",
"client_email": "build-ci-mine#myproj.iam.gserviceaccount.com",
"client_id": "1167333552",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/build-ci-mine%40myproj.iam.gserviceaccount.com"
}
oops, my edit didn't get posted :(. Here is the error
Exception in thread "main" java.io.IOException: Server returned HTTP response code: 401 for URL: https://cloudbuild.googleapis.com/v1/projects/orderly-gcp/builds
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1919)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1515)
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:250)
at com.orderlyhealth.auth.websecure.GenToken.makeJwtRequest(GenToken.java:71)
at com.orderlyhealth.auth.websecure.GenToken.main(GenToken.java:26)
I hope that I better understood!!
When you try to reach a Google API, you have to use an access token. I have 2 code snippets for you.
Use Google Http client
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
HttpRequestFactory factory = new NetHttpTransport().createRequestFactory(new HttpCredentialsAdapter(credentials));
HttpRequest request = factory.buildGetRequest(new GenericUrl("https://cloudbuild.googleapis.com/v1/projects/gbl-imt-homerider-basguillaueb/builds"));
HttpResponse httpResponse = request.execute();
System.out.println(CharStreams.toString(new InputStreamReader(httpResponse.getContent(), Charsets.UTF_8)));
Use pure java connection
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
HttpURLConnection con = (HttpURLConnection) new URL("https://cloudbuild.googleapis.com/v1/projects/gbl-imt-homerider-basguillaueb/builds").openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Authorization", "Bearer " + credentials.refreshAccessToken().getTokenValue());
InputStreamReader reader = new InputStreamReader(con.getInputStream());
BufferedReader buffReader = new BufferedReader(reader);
String line;
StringBuilder result = new StringBuilder();
while ((line = buffReader.readLine()) != null) {
result.append(line);
}
buffReader.close();
System.out.println(result.toString());
You can rely on the platform environment. In local, perform a gcloud auth application-default login to set your credential as default default credential. On GCP, the component identity (the default service account or the service account that you define when you create the component), is used thanks to the method GoogleCredentials.getApplicationDefault();
Your dependency management need this (here in maven)
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>0.20.0</version>
</dependency>
Does this solve your issue?

JMeter throws error 'Signature doesn't match' while uploading file to s3

Can anyone help me figure out what is happening?
I am trying to load test an application where a user can upload and download files.
Journey:
Step-1:When a user choose a file from disk (POST request) it creates a fileID and path with uuid.
Response looks like:
{"id":"FILE-VX-1234","path":"uuid/filename.jpg","uri":["s3://{location}/{uuid}/{filename}?endpoint=s3.dualstack.eu-west-1.amazonaws.com"],"state":"OPEN","size":-1,"timestamp":"2020-02-13T10:59:43.146+0000","refreshFlag":1,"storage":"STORAGEID","metadata":{}
Step-2:Using these (POST request) which responds with a s3 uri with assesskeyID, secretaccesskey and sessionToken.
Response looks like:
{"uri":["s3://{accesskeyID}:{secretaccesskey}#{storage location}/{uuid}/{filename}?endpoint=s3.dualstack.eu-west-1.amazonaws.com&sessionToken={security token}"]}
Step-3:Using these and added temporary parameters (date), a PUT request uploads a file in s3 bucket.
Header looks like:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Authorization: AWS4-HMAC-SHA256 Credential=${accesskeyID}/${currentDate}/{region}/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=${secretaccesskey}
Connection: keep-alive
Content-Length: 145541
Content-Type: image/jpeg
Host: <the host address>
Origin: https://{url}
Referer: https://{url}/upload/
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
X-Amz-Content-Sha256: UNSIGNED-PAYLOAD
X-Amz-Date:${currentDateInUTC}
x-amz-security-token: ${sessionToken}
X-Amz-User-Agent: aws-sdk-js/2.409.0 callback
Error:
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
I have verified that the signature(secretaccesskey), accesskeyID and sessionToken which are passed in PUT request are correct.
Note: Additional parameters date and "{region}/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent" in Authorisation error are hardcoded.
It is impossible to provide any assistance without seeing how do you generate the signature for the request, to wit this Authorization header
As per Signing and Authenticating REST Requests article
The Amazon S3 REST API uses a custom HTTP scheme based on a keyed-HMAC (Hash Message Authentication Code) for authentication. To authenticate a request, you first concatenate selected elements of the request to form a string. You then use your AWS secret access key to calculate the HMAC of that string. Informally, we call this process "signing the request," and we call the output of the HMAC algorithm the signature, because it simulates the security properties of a real signature. Finally, you add this signature as a parameter of the request by using the syntax described in this section.
Here is a pseudo-code demonstrating how the header needs to be generated:
Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
Signature = Base64( HMAC-SHA1( YourSecretAccessKey, UTF-8-Encoding-Of( StringToSign ) ) );
StringToSign = HTTP-Verb + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Date + "\n" +
CanonicalizedAmzHeaders +
CanonicalizedResource;
CanonicalizedResource = [ "/" + Bucket ] +
<HTTP-Request-URI, from the protocol name up to the query string> +
[ subresource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
CanonicalizedAmzHeaders = <described below>
You can check How to Handle Dynamic AWS SigV4 in JMeter for API Testing article for example implementation.
The AWS is now supporting the Signature version 4 which requires to be generated while uploading file to S3 bucket.
There are two type of credential you can use while upload:
Service credential which are combination of: accessKey, secretKey, serviceName and region
Temporary credential which are combination of: accessKey, secretKey, serviceName, sessionToken and region
In both of the cases you need to add JSR223 Preprocessor under the HTTP Request Sampler.
Add the below code to JSR223 Preprocessor and provide the required details:
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.security.InvalidKeyException
import java.security.MessageDigest
import groovy.json.JsonSlurper
import java.text.SimpleDateFormat
//Defined in User Defined Variables
def access_key = <access key>
def secret_key = <secret key>
def host = <host>
def service = "s3"
def region = "us-east-1"
def token = <session token>
def localfile = <absolute path for local file>
def fileData = readBinaryFile(localfile)
//Create SHA256 of file data
def contentSha256 = org.apache.commons.codec.digest.DigestUtils.sha256Hex(fileData)
vars.put("aws_sha256", contentSha256)
vars.put("aws_content_length", fileData.length + "")
log.info("access key: " + access_key)
log.info("secret_key: " + secret_key)
log.info("host: " + host)
log.info("service: " + service)
log.info("region: " + region)
log.info("file: " + props.get("file_1.jpg"))
log.info("file sha256: " + contentSha256)
log.info("token: " + token)
log.info("content-length: " + fileData.length)
//Obtain data form the Http Request Sampler
def method = sampler.getMethod()
def url = sampler.getUrl()
def req_path = url.getPath()
def req_query_string = orderQuery(url)
def request_parameters = '';
sampler.getArguments().each {arg ->
request_parameters = arg.getStringValue().substring(1)
}
//Create the variable x-amz-date
def now = new Date()
def amzFormat = new SimpleDateFormat( "yyyyMMdd'T'HHmmss'Z'" )
def stampFormat = new SimpleDateFormat( "yyyyMMdd" )
amzFormat.setTimeZone(TimeZone.getTimeZone("UTC")); //server timezone
def amzDate = amzFormat.format(now)
def dateStamp = stampFormat.format(now)
vars.put("x_amz_date", amzDate)
//Create a Canonical Request
def canonical_uri = req_path
def canonical_querystring = req_query_string
def canonical_headers = "host:" + host + "\n" + "x-amz-date:" + amzDate + "\n"
canonical_headers = "host:" + host + "\n" + "x-amz-content-sha256:" + contentSha256 + "\n" + "x-amz-date:" + amzDate + "\n" + "x-amz-security-token:" + token + "\n"
//def signed_headers = "host;x-amz-date"
def signed_headers = "host;x-amz-content-sha256;x-amz-date;x-amz-security-token"
def payload_hash = getHexDigest(request_parameters)
def canonical_request = method + "\n" + canonical_uri + "\n" + canonical_querystring + "\n" + canonical_headers + "\n" + signed_headers + "\n" + contentSha256
log.info("---------canonical_request-------: " + canonical_request)
//Create the String to Sign
def algorithm = "AWS4-HMAC-SHA256"
def credential_scope = dateStamp + "/" + region + "/" + service + "/" + "aws4_request"
def hash_canonical_request = getHexDigest(canonical_request)
def string_to_sign = algorithm + "\n" + amzDate + "\n" + credential_scope + "\n" + hash_canonical_request
log.info("------string to sign-----: " + string_to_sign)
//Calculate the String to Sign
log.info("------datestamp to sign----: "+ dateStamp)
def signing_key = getSignatureKey(secret_key, dateStamp, region, service)
log.info("------key to sign----: "+ org.apache.commons.codec.digest.DigestUtils.sha256Hex(signing_key))
def signature = hmac_sha256Hex(signing_key, string_to_sign)
//Add Signing information to Variable
def authorization_header = algorithm + " " + "Credential=" + access_key + "/" + credential_scope + ", " + "SignedHeaders=" + signed_headers + ", " + "Signature=" + signature
vars.put("aws_authorization", authorization_header)
def hmac_sha256(secretKey, data) {
Mac mac = Mac.getInstance("HmacSHA256")
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256")
mac.init(secretKeySpec)
byte[] digest = mac.doFinal(data.getBytes())
return digest
}
def hmac_sha256Hex(secretKey, data) {
def result = hmac_sha256(secretKey, data)
return result.encodeHex()
}
def getSignatureKey(key, dateStamp, regionName, serviceName) {
def kDate = hmac_sha256(("AWS4" + key).getBytes(), dateStamp)
def kRegion = hmac_sha256(kDate, regionName)
def kService = hmac_sha256(kRegion, serviceName)
def kSigning = hmac_sha256(kService, "aws4_request")
return kSigning
}
def getHexDigest(text) {
def md = MessageDigest.getInstance("SHA-256")
md.update(text.getBytes())
return md.digest().encodeHex()
}
public static String orderQuery(URL url) throws UnsupportedEncodingException {
def orderQueryString = "";
Map<String, String> queryPairs = new LinkedHashMap<>();
String queryParams = url.getQuery();
if (queryParams != null) {
String[] pairs = queryParams.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
queryPairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
}
def orderQueryArray = new TreeMap<String, String>(queryPairs);
orderQueryString = urlEncodeUTF8(orderQueryArray)
}
return orderQueryString;
}
public static String urlEncodeUTF8(String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new UnsupportedOperationException(e);
}
}
public static String urlEncodeUTF8(Map<?,?> map) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<?,?> entry : map.entrySet()) {
if (sb.length() > 0) {
sb.append("&");
}
sb.append(String.format("%s=%s",
urlEncodeUTF8(entry.getKey().toString()),
urlEncodeUTF8(entry.getValue().toString())
));
}
return sb.toString();
}
public static byte[] readBinaryFile(String filePath) {
File file = new File(filePath)
byte[] binaryContent = file.bytes
return binaryContent
}
Provide the below parameters into the preprocessor:
def access_key = <access key>
def secret_key = <secret key>
def host = <host>
def service = "s3" //is any specific name given for service else it will be s3 by default
def region = "us-east-1" //the required region
def token = <session token>
def localfile = <absolute path for local file>
Then add the HTTP Header Manager into the HTTP Request Sampler where add the below headers:
Header Name: Value
X-Amz-Date: ${x_amz_date}
X-Amz-Security-Token: ${sessionToken}
X-Amz-Content-Sha256: ${aws_sha256}
Authorization: ${aws_authorization}
Content-Length: ${aws_content_length}
Content-Type: <file content type>
Host: ${host}
Here, below header's value calculated dynamically when file comes to upload by the preprocessor:
X-Amz-Date
X-Amz-Content-Sha256
Authorization
Content-Length
Now, In HTTP Request sampler set the request method (normally PUT), protocol (e.g. https), server (...s3.service.com), port (e.g. 443), path (path of the file including bucket name).
Click on File Upload tab in HTTP Request Sampler, profile the absolute path of local file which need to be uploaded and provide the mime type.
Note: Do not provide parameter name (it will be blank as file will go in raw binary format) and Use multipart/form data checkbox should be unchecked as file is going in raw format.
Just to give a little more help, you can use JSR223 Sampler for making HTTP request with different file upload / download approach. E.g. Uploading dynamically calculated data as file. Refer the link: How to pass data from JSR223 sampler to Http Request sampler in JMeter
Hope it will help you.

Windows.Web.HttpClient: Cookies will not be sent

I send a HTTP Get Request with a Basic Authentification to the Login-Endpoint of the Host:
request = new HttpRequestMessage();
// Configuration Item: Login URL Suffix
request.RequestUri = new Uri(string.Format("https://{0}/{1}", Host, loginSuffix));
request.Method = Windows.Web.Http.HttpMethod.Get;
var info = User + ":" + Password;
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes(info));
request.Headers.Authorization = new HttpCredentialsHeaderValue("Basic", token);
_httpClient = CreateHttpClient(ref cookieManager);
response = await _httpClient.SendRequestAsync(request, HttpCompletionOption.ResponseContentRead);
response.EnsureSuccessStatusCode();
responseBodyAsText = await response.Content.ReadAsStringAsync();
The HttpClient is created with a Filter, to set Cookies later:
private HttpClient CreateHttpClient(ref HttpCookieManager _cookieManager)
{
HttpBaseProtocolFilter _filter = new HttpBaseProtocolFilter();
HttpClient _httpClient = new Windows.Web.Http.HttpClient(_filter);
_cookieManager = _filter.CookieManager;
return _httpClient;
}
From the Response the SET-COOKIE Header can be read.
string[] Queries;
response.Headers.TryGetValue("Set-Cookie", out tmpString);
if (tmpString != null)
Queries = tmpString.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
I´m looking for a Cookie with a defined Name (CookieKeyName), which will be set in the next Request.
foreach (var query in Queries)
{
if (query.Contains(CookieKeyName))
{
staticCookieKey = query.Substring(0, query.IndexOf("="));
staticCookieValue = query.Substring(query.IndexOf("=") + 1);
}
}
I would expect, that the HttpClient will use the received Set-Cookie in the response for this URL as Cookie in every following Request automatically.
I´m preparing the next Request and setting the Cookie by myself:
request.RequestUri = new Uri(string.Format("https://{0}/qcbin", Host));
request.Method = Windows.Web.Http.HttpMethod.Get;
HttpCookie _cookie = new Windows.Web.Http.HttpCookie(staticCookieKey, Host, "/");
_cookie.Value = staticCookieValue;
bool replaced = cookieManager.SetCookie(_cookie);
The following Sending of the Requests provides to a Web Exception 401, because the Server expects for this URL the previously in the Response received Cookie.
response = await _httpClient.SendRequestAsync(request, HttpCompletionOption.ResponseContentRead);
response.EnsureSuccessStatusCode();
responseBodyAsText = await response.Content.ReadAsStringAsync();
Looking with Fiddler on the Line, the second Request contains no Cookie Header. Even Setting of the Cookie in CookieManager nor the proceeding of the Set-Cookie i the first Response by the HttpClient is working.
Hint: The length of the value of the Cookies is about 6000 chars (coming from a IBM Data Power).
Thank you in advance for help.

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

C# Restful Service Using WebInvoke POST

I am developing a Restful service in C# and working well when I use
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Xml, BodyStyle =
WebMessageBodyStyle.Wrapped, UriTemplate = "json/?id={id}")]
string jdata(string id);
and my corrsponding function implementation is:
public string json(string id)
{
return "You Typed : "+id;
}
Up to here all works well,
but when I change WenInvoke Method="POST" I have to face a "Method NOT Allowed.";
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Xml, BodyStyle =
WebMessageBodyStyle.Wrapped, UriTemplate = "json/?id={id}")]
string jdata(string id);
You get "Method not allowed" because you are reaching the Uri "json/?id={id}" via GET instead of POST.
Check this with your client (you didn't mention how you call this resource). Please give some further details how you are trying to use your web service in client. Is is .Net client?
To test your API I recommend using Fiddler - when you can explicitly specify whether to use GET or POST before sending an http request:
Another thing is, you might have unwittingly used "json" as Uri, but defined ResponseFormat as WebMessageFormat.Xml. Isn't it a little confusing for the client? Maybe you wanted to return JSON back? In that case, I would recommend using Json in both - request and response:
[WebInvoke(Method = "POST", UriTemplate = "/ValidateUser", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
[WebInvoke(Method="POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "json")]
string jdata(string id);
This is how your contract should look like and then in client
WebRequest httpWebRequest =
WebRequest.Create(
url);
httpWebRequest.Method = "POST";
string json = "{\"id\":\"1234"\}"
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
streamWriter.Write(json);
}
httpWebRequest.Timeout = 1000000;
WebResponse webrespon = (WebResponse)httpWebRequest.GetResponse();
StreamReader stream = new StreamReader(webrespon.GetResponseStream());
string result = stream.ReadToEnd();
Console.Out.WriteLine(result);
Above is just something i use to test my services. Hope it helps.