Here Map API request token with NodeJS - node.js

I am trying to request a token to the Here Map API with NodeJS in order to obtain OAuth 2.0 Token Credentials.
I am blocked a the request level and constantly having the same error but according to the documentation I don't do anything wrong.
Here is the necessary code in NodeJS to make the request :
const keyID = "MyKeyID";
const keySecret = "MySecretKey";
const httpMethod = "POST";
const httpUrl = "https://account.api.here.com/oauth2/token";
let oauthNonce = Math.random().toString(36).substring(6);
let oauthTimestamp = Date.now();
let oauthSignatureMethod = "HMAC-SHA256";
let oauthVersion = "1.0";
let baseString = httpMethod + "&" + httpUrl;
let oauth1Param = [];
oauth1Param.oauth_consumer_key = keyID;
oauth1Param.oauth_signature_method = oauthSignatureMethod;
oauth1Param.oauth_signature = "";
oauth1Param.oauth_timestamp = oauthTimestamp;
oauth1Param.oauth_nonce = oauthNonce;
oauth1Param.oauth_version = oauthVersion;
let paramString = "grant_type=client_credentials"
+ "&oauth_consumer_key=" + oauth1Param.oauth_consumer_key
+ "&oauth_nonce=" + oauth1Param.oauth_nonce
+ '&oauth_signature_method=' + oauth1Param.oauth_signature_method
+ "&oauth_timestamp=" + oauth1Param.oauth_timestamp
+ "&oauth_version=" + oauth1Param.oauth_version;
baseString += "&" + paramString;
let signingKey = keySecret + "&";
let signature = crypto.createHmac('SHA256', signingKey).update(baseString).digest('base64');
oauth1Param.oauth_signature = signature;
console.log(JSON.stringify(paramString));
console.log(oauth1Param);
let headerOauth = "OAuth ";
for (let key in oauth1Param) {
headerOauth += key + "=" + oauth1Param[key] + ",";
}
headerOauth = headerOauth.slice(0, -1);
console.log(headerOauth);
var request = require('request');
request.post({
url : httpUrl,
headers: {
'content-type' : 'application/x-www-form-urlencoded',
'Authorization' : headerOauth
},
body : JSON.stringify({
"grant_type" : "client_credentials"
})
}, function(error, response, body){
if(error){
console.log("------ ERROR -------");
throw error;
}
//console.log(response);
console.log(body);
});
And this is the error that I keep getting :
{"errorId":"ERROR-51fa3a57-1dfa-4da7-b2e9-5f94574a4f75",
"httpStatus":401,
"errorCode":401205,
"message":"Unsupported signature method in the header. Require HMAC-SHA256",
"error":"invalid_request",
"error_description":"errorCode: '401205'. Unsupported signature method in the header. Require HMAC-SHA256"}
According to the documentation, https://developer.here.com/documentation/authentication/dev_guide/topics/error-messages.html, I have an error with the signature method and that it's required to be HMAC-SHA256 but actually this is what I have put at the beginning of my code : let oauthSignatureMethod = "HMAC-SHA256";
Does anyone know how to fix this issue?

The library you're using, request, supports OAuth signing. Therefore the code can be significantly shorter:
const request = require('request');
const OAUTH_URL = 'https://account.api.here.com/oauth2/token';
const KEY_ID = '<KEY_ID>';
const KEY_SECRET = '<KEY_SECRET>';
request.post({
url: OAUTH_URL,
oauth: {
consumer_key: KEY_ID,
consumer_secret: KEY_SECRET,
signature_method: 'HMAC-SHA256'
},
headers: {
'Content-Type' : 'application/x-www-form-urlencoded',
},
form: {
'grant_type': 'client_credentials'
}
}, function (e, r, body) {
console.log(body);
});
As for your code snippet, JSON.stringify(obj) is not appropriate as it will encode the curly bracket of the object as well.
Additionally, the values in the OAuth header need to be surrounded by double quotes. It's easy to get little things wrong with OAuth :)

Related

How to authorize with AWS signature 4 -> API Gateway -> Lambda

I've googled around a lot with no luck in finding the solution to my problem. I've read through the entire authentication process for AWS Signature 4 and followed their tutorial as well as view other sources. I'm trying to have client side authentication for a desktop application that makes request to API Gateway.
When I use Postman it works properly but I tried generating my own signature in Nodejs but to no avail, I keep getting 403 messages back from the call.
The function below returns the authenticated requestUrl which is then run by axios.get(requestUrl). When I use the Postman generated request it works perfectly fine but, once I use my generated request I have problems.
Am I missing something while authenticating? Here is what my code currently looks like:
function Authorize() {
const host = "EXAMPLE.execute-api.us-east-1.amazonaws.com"
const reg = 'us-east-1'
const meth = 'GET'
const serv = 'execute-api'
const endpoint = '/development/putImage'
// Keys
let access = "EXAMPLE"
let key = "KEY"
// Get Date
let t = new Date();
let amzDate = t.toJSON().replace(/[-:]/g, "").replace(/\.[0-9]*/, "");
let dateStamp = t.toJSON().replace(/-/g, "").replace(/T.*/, "");
// ************* TASK 1: CREATE CANONICAL REQUEST *************
// Create Canonical Request
let canonical_uri=endpoint
let canonical_headers="host: "+host+"\n"
let signedHeaders = 'host'
let algorithm = 'AWS4-HMAC-SHA256'
let credentialScope = dateStamp + "/" + reg + "/" + serv + "/" + "aws4_request"
// Set query string
let canonicalQueryString = ""
canonicalQueryString += "X-Amz-Date=" + amzDate
canonicalQueryString += "&X-Amz-Algorithm=" + algorithm;
canonicalQueryString += "&X-Amz-Credential=" + encodeURIComponent(access + "/" + credentialScope)
canonicalQueryString += "&X-Amz-SignedHeaders=" + signedHeaders
// Empty payload for get request
var payloadHash = crypto.createHash('sha256').update('').digest('hex');
// Set canonical request
var canonicalRequest = meth + "\n" + canonical_uri + "\n" + canonicalQueryString + "\n" + canonical_headers + "\n" + signedHeaders + "\n" + payloadHash
console.log(canonicalRequest)
// ************* TASK 2: CREATE THE STRING TO SIGN*************
let stringToSign = algorithm + '\n' + amzDate + '\n' + credentialScope + '\n' + crypto.createHash('sha256').update(canonicalRequest).digest('hex');
// ************* TASK 3: CALCULATE THE SIGNATURE *************
var signingKey = getSignatureKey(key, dateStamp, reg, serv)
var signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');
// ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
canonicalQueryString += '&X-Amz-Signature=' + signature
let requestUrl = "https://"+host+ endpoint + "?" + canonicalQueryString
console.log(requestUrl)
return requestUrl
}
The below code worked for me well. For more info, please visit https://docs.aws.amazon.com/opensearch-service/latest/developerguide/request-signing.html#request-signing-node
const { HttpRequest} = require("#aws-sdk/protocol-http");
const { defaultProvider } = require("#aws-sdk/credential-provider-node");
const { SignatureV4 } = require("#aws-sdk/signature-v4");
const { NodeHttpHandler } = require("#aws-sdk/node-http-handler");
const { Sha256 } = require("#aws-crypto/sha256-browser");
...
var request = new HttpRequest({
body: JSON.stringify({"users":["G0000000B","G0000000A"]}),
headers: {
'Content-Type': 'application/json',
'apiKey':'XXXXXXXXXXXX',
'apiSecret': 'XXXXXXXXXXXXXXXXXX',
'host': 'service2.xxx.xxx.xx'
},
hostname: 'service2.xxx.xxx.xx',
method: 'POST',
path: 'API/user/list'
});
var signer = new SignatureV4({
credentials: defaultProvider(),
region: 'ap-southeast-1',
service: 'execute-api',
sha256: Sha256
});
const signedRequest = await signer.sign(request);
// Send the request
var client = new NodeHttpHandler();
var { response } = await client.handle(signedRequest)
console.log(response.statusCode + ' ' + response.body.statusMessage);
var responseBody = '';
await new Promise(() => {
response.body.on('data', (chunk) => {
responseBody += chunk;
});
response.body.on('end', () => {
console.log('Response body: ' + responseBody);
});
}).catch((error) => {
console.log('Error: ' + error);
});

How to scheduling push notifications using azure sdk for node

I know it is possible in .net, i can see the reference over here https://learn.microsoft.com/en-us/azure/notification-hubs/notification-hubs-send-push-notifications-scheduled. But I want to know how to do that in node. Can any one guide me on this.
You can send a scheduled notification in Node using the REST API. Use the specification for sending a normal notification and replace /messages with /schedulednotifications. You will also need to add a header specifying the datetime named ServiceBusNotification-ScheduleTime.
For an example using the template schema:
var CryptoJS = require("crypto-js");
var axios = require("axios");
var getSelfSignedToken = function(targetUri, sharedKey, keyName,
expiresInMins) {
targetUri = encodeURIComponent(targetUri.toLowerCase()).toLowerCase();
// Set expiration in seconds
var expireOnDate = new Date();
expireOnDate.setMinutes(expireOnDate.getMinutes() + expiresInMins);
var expires = Date.UTC(expireOnDate.getUTCFullYear(), expireOnDate
.getUTCMonth(), expireOnDate.getUTCDate(), expireOnDate
.getUTCHours(), expireOnDate.getUTCMinutes(), expireOnDate
.getUTCSeconds()) / 1000;
var tosign = targetUri + '\n' + expires;
// using CryptoJS
var signature = CryptoJS.HmacSHA256(tosign, sharedKey);
var base64signature = signature.toString(CryptoJS.enc.Base64);
var base64UriEncoded = encodeURIComponent(base64signature);
// construct autorization string
var token = "SharedAccessSignature sr=" + targetUri + "&sig="
+ base64UriEncoded + "&se=" + expires + "&skn=" + keyName;
// console.log("signature:" + token);
return token;
};
var keyName = "<mykeyName>";
var sharedKey = "<myKey>";
var uri = "https://<mybus>.servicebus.windows.net/<myhub>";
var expiration = 10;
var token = getSelfSignedToken(uri, sharedKey, keyName, expiration);
const instance = axios.create({
baseURL: uri,
timeout: 100000,
headers: {
'Content-Type': 'application/octet-stream',
'X-WNS-Type': 'wns/raw',
'ServiceBusNotification-Format' : 'template',
'ServiceBusNotification-ScheduleTime': '2019-07-19T17:13',
'authorization': token}
});
var payload = {
"alert" : " This is my test notification!"
};
instance.post('/schedulednotifications?api-version=2016-07', payload)
.then(function (response) {
console.log(response);
}).catch(function (error) {
// handle error
console.log(error);
});

How to generate oauth_token from twitter request token API

I'm creating an app in which I need to post tweets in twitter on behalf of different users. So, I using Application User authentication. For me to redirect the user to twitter page to provide access I need oauth_token from twitter.
For that I am using Request_token_api.
But everytime am getting could not authenticate you and the twitter api page says authorization is not required for this API
I tried using many libraries like passport, Twit, OAuth, etc.. But nothing worked and I am pretty sure all my developer account consumer_key, consumer_secret, access_token, access_token_secret are correct.
function getAuthorization(httpMethod, baseUrl, reqParams) {
// Get acces keys
const consumerKey = keysJson.TWITTER_CONSUMER_KEY,
consumerSecret = keysJson.TWITTER_CONSUMER_SECRET,
accessToken = keysJson.TWITTER_ACCESS_TOKEN,
accessTokenSecret = keysJson.TWITTER_ACCESS_TOKEN_SECRET;
// timestamp as unix epoch
let timestamp = Math.round(Date.now() / 1000);
// nonce as base64 encoded unique random string
let nonce = crypto.randomBytes(32).toString('base64').replace(/[^0-9a-zA-Z]/g, '') ;
let cburl = percentEncode(keysJson.cb);
// generate signature from base string & signing key
let baseString = oAuthBaseString(httpMethod, baseUrl, reqParams, consumerKey, accessToken, timestamp, nonce);
console.log(baseString);
let signingKey = oAuthSigningKey(consumerSecret, accessTokenSecret);
// console.log(signingKey);
let signature = oAuthSignature(baseString, signingKey);
console.log(signature);
// return interpolated string
return 'OAuth ' +
'oauth_nonce="' + nonce + '", ' +
'oauth_callback="' + cburl + '", ' +
'oauth_signature_method="HMAC-SHA1", ' +
'oauth_timestamp="' + timestamp + '", ' +
'oauth_consumer_key="' + consumerKey + '", ' +
'oauth_signature="' + signature + '", ' +
'oauth_version="1.0"' ;
}
function oAuthBaseString(method, url, params, key, token, timestamp, nonce) {
return method
+ '&' + percentEncode(url)
+ '&' + percentEncode(genSortedParamStr(params, key, token, timestamp, nonce));
};
function oAuthSigningKey(consumer_secret, token_secret) {
return consumer_secret + '&' + token_secret;
};
function oAuthSignature(base_string, signing_key) {
var signature = hmac_sha1(base_string, signing_key);
return percentEncode(signature);
};
function percentEncode(str) {
return encodeURIComponent(str).replace(/[!*()']/g, (character) => {
return '%' + character.charCodeAt(0).toString(16);
});
};
function hmac_sha1(string, secret) {
let shaObj = new jsSHA("SHA-1", "TEXT");
shaObj.setHMACKey(secret, "TEXT");
shaObj.update(string);
let hmac = shaObj.getHMAC("B64");
return hmac;
};
function mergeObjs(obj1, obj2) {
for (var attr in obj2) {
obj1[attr] = obj2[attr];
}
return obj1;
};
function genSortedParamStr(params, key, token, timestamp, nonce) {
// Merge oauth params & request params to single object
let paramObj = mergeObjs(
{
include_entities: 'true',
oauth_consumer_key : key,
oauth_nonce : nonce,
oauth_signature_method : 'HMAC-SHA1',
oauth_timestamp : timestamp,
// oauth_token : token,
oauth_version : '1.0'
},
);
// Sort alphabetically
let paramObjKeys = Object.keys(paramObj);
let len = paramObjKeys.length;
paramObjKeys.sort();
// Interpolate to string with format as key1=val1&key2=val2&...
let paramStr = paramObjKeys[0] + '=' + paramObj[paramObjKeys[0]];
for (var i = 1; i < len; i++) {
paramStr += '&' + paramObjKeys[i] + '=' + percentEncode(decodeURIComponent(paramObj[paramObjKeys[i]]));
}
return paramStr;
};
var redirect_uri = encodeURIComponent('http://localhost:4200/admin/marketing/loginsuccess');
var auth = getAuthorization('POST','https://api.twitter.com/oauth/request_token',{oauth_callback:redirect_uri});
console.log(auth);
var options = {
"method": "POST",
"hostname": "api.twitter.com",
"port": null,
"path": '/oauth/request_token',
"headers": {
'Authorization' : auth
}
};
try {
console.log(options);
var req = https.request(options, function (res1) {
var chunks = [];
console.log('statusCode:', res1.statusCode);
console.log('headers:', res1.headers);
res1.on("data", function (chunk) {
chunks.push(chunk);
});
res1.on("end", function () {
var body = Buffer.concat(chunks);
var disp = JSON.parse(body.toString());
console.log(disp);
});
});
req.end();
} catch (error) {
console.log(error);
res.json({success:false,message:error});
}
I need the oauth access token from Twitter API but am getting.
{ errors: [ { code: 32, message: 'Could not authenticate you.' } ] }
I am struggling for 3 days. Kindly let me know is there any alternate way to implement twitter API in nodejs or some sample code in github.
Thanks in advance..

windows azure : set blob CORS properties using NodeJS

REST API has been released in february to set blob CORS property, but this hasn't been implemented for NodeJS yet.
Since I need this feature, I tried to implement it in a module for my azure website running NodeJS.
Based on REST API documentation to change CORS properties and to generate authentification key, on this implementation of authentification key generation using NodeJS, I tried to follow the accepted answer from this post, but it didn't work for me.
Here is what I've got in setcrosproperties.js :
var crypto = require('crypto');
var request = require('request');
exports.setCors = function (MY_ACCOUNT_URL, MY_ACCOUNT_NAME, MY_ACCOUNT_HOST, accountKey) {
var MY_CORS_XML =
'<?xml version="1.0" encoding="utf-8"?>'+
'<StorageServiceProperties>'+
'<Cors>'+
'<CorsRule>'+
'<AllowedOrigins>*</AllowedOrigins>'+
'<AllowedMethods>GET,PUT</AllowedMethods>'+
'<MaxAgeInSeconds>500</MaxAgeInSeconds>'+
'<ExposedHeaders>x-ms-meta-data*,x-ms-meta-customheader</ExposedHeaders>'+
'<AllowedHeaders>x-ms-meta-target*,x-ms-meta-customheader</AllowedHeaders>'+
'</CorsRule>'+
'</Cors>'+
'<DefaultServiceVersion>2013-08-15</DefaultServiceVersion>'+
'</StorageServiceProperties>';
var url = MY_ACCOUNT_URL + '/?restype=service&comp=properties';
var canonicalizedResource = '/' + MY_ACCOUNT_NAME + '/?comp=properties';
var corsMD5 = crypto.createHash('md5' ).update(MY_CORS_XML).digest('base64');
var date = (new Date()).toUTCString();
var headers = {
'x-ms-version': '2013-08-15',
'x-ms-date': date,
'Host': MY_ACCOUNT_HOST
};
var canonicalizedHeaders = buildCanonicalizedHeaders( headers );
// THIS
var key = buildSharedKeyLite( 'PUT', corsMD5, 'text/plain; charset=UTF-8', canonicalizedHeaders, canonicalizedResource, accountKey);
// AND THIS, BOTH YIELD THE SAME SERVER RESPONSE
// var key = buildSharedKeyLite( 'PUT', "", "", canonicalizedHeaders, canonicalizedResource, accountKey);
headers['Authorization'] = 'SharedKeyLite ' + MY_ACCOUNT_NAME + ':' + key;
var options = {
url: url,
body: MY_CORS_XML,
headers: headers
};
console.log("url : " + url);
console.log("canonicalizedResource : " + canonicalizedResource);
console.log("canonicalizedHeaders : " + canonicalizedHeaders);
console.log("corsMD5 : " + corsMD5);
console.log("key : " + key);
console.log("options : " + JSON.stringify(options));
function onPropertiesSet(error, response, body) {
if (!error && response.statusCode == 202) {
console.log("CORS: OK");
}
else {
console.log("CORS: " + response.statusCode);
console.log("body : " + body);
}
}
request.put(options, onPropertiesSet); // require('request')
};
function buildCanonicalizedHeaders( headers ) {
var xmsHeaders = [];
var canHeaders = "";
for ( var name in headers ) {
if ( name.indexOf('x-ms-') == 0 ) {
xmsHeaders.push( name );
}
}
xmsHeaders.sort();
for ( var i = 0; i < xmsHeaders.length; i++ ) {
name = xmsHeaders[i];
canHeaders = canHeaders + name.toLowerCase().trim() + ':' + headers[name] + '\n';
}
return canHeaders;
}
function buildSharedKeyLite( verb, contentMD5, contentType, canonicalizedHeaders, canonicalizedResource, accountKey) {
var stringToSign = verb + "\n" +
contentMD5 + "\n" +
contentType + "\n" +
"" + "\n" + // date is to be empty because we use x-ms-date
canonicalizedHeaders +
canonicalizedResource;
// return crypto.createHmac('sha256', accountKey).update(encodeURIComponent(stringToSign)).digest('base64');
return crypto.createHmac('sha256', new Buffer(accountKey, 'base64')).update(stringToSign).digest('base64');
}
And here is how I call this function from my server.js file :
var setcrosproperties = require('./setcrosproperties.js');
// setCors(MY_ACCOUNT_URL, MY_ACCOUNT_NAME, MY_ACCOUNT_HOST, accountKey)
setcrosproperties.setCors(
'https://'+process.env['AZURE_STORAGE_ACCOUNT']+'.blob.core.windows.net',
process.env['AZURE_STORAGE_ACCOUNT'],
process.env['AZURE_STORAGE_ACCOUNT']+'.blob.core.windows.net',
process.env['AZURE_STORAGE_ACCESS_KEY']);
I did not understand what was the difference intended with variables MY_ACCOUNT_UTL (I assumed URL) and MY_ACCOUNT_HOST, so I use the same value for both parameters of the function.
(I removed the "cors" parameter, which seemed to be unused.)
Here is what I get in the console :
url : https://NAME_OF_MY_STORAGE_ACCOUNT.blob.core.windows.net/?restype=service&comp=properties
canonicalizedResource : /NAME_OF_MY_STORAGE_ACCOUNT/?comp=properties
canonicalizedHeaders : x-ms-date:Sun, 09 Mar 2014 12:33:41 GMT
x-ms-version:2013-08-15
corsMD5 : +ij...w==
key : sNB...JrY=
options : {"url":"https://NAME_OF_MY_STORAGE_ACCOUNT.blob.core.windows.net/?restype=service&comp=properties","body":"GET,PUT500x-ms-meta-data,x-ms-meta-customheaderx-ms-meta-target*,x-ms-meta-customheader2013-08-15","headers":{"x-ms-version":"2013-08-15","x-ms-date":"Sun, 09 Mar 2014 12:33:41 GMT","Host":"NAME_OF_MY_STORAGE_ACCOUNT.blob.core.windows.net","Authorization":"SharedKeyLite NAME_OF_MY_STORAGE_ACCOUNT:sNB...rY="}}
CORS: 403
body : AuthenticationFailedServer failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:1e6abfe3-e0e8-4b9c-922d-7cb34485eec9
Time:2014-03-09T12:33:41.7262308ZThe MAC signature found in the HTTP request 'sNB...JrY=' is not the same as any computed signature. Server used following string to sign: 'PUT
x-ms-date:Sun, 09 Mar 2014 12:33:41 GMT
x-ms-version:2013-08-15
/NAME_OF_MY_STORAGE_ACCOUNT/?comp=properties'.
Any idea about what I am doing wrong here? Thanks for your help
To configure CORS, use the Azure storage library for Node.js.
You can do npm install azure-storage to get it. Source code is at https://github.com/Azure/azure-storage-node.
The one that come with npm package azure (a.k.a. azure-sdk-for-node) use older azure-storage-legacy package, which does not support CORS.
You can set the CORS with the following code:
var service = azure.createBlobService();
var serviceProperties = {
Cors: {
CorsRule: [{
AllowedOrigins: ['*'],
AllowedMethods: ['GET'],
AllowedHeaders: [],
ExposedHeaders: [],
MaxAgeInSeconds: 60
}]
}
};
service.setServiceProperties(serviceProperties, callback);
Please add Content-Type and Content-MD5 in your headers array and that should do the trick. Here's the modified code:
var crypto = require('crypto');
var request = require('request');
exports.setCors = function (MY_ACCOUNT_URL, MY_ACCOUNT_NAME, MY_ACCOUNT_HOST, accountKey) {
var MY_CORS_XML =
'<?xml version="1.0" encoding="utf-8"?>'+
'<StorageServiceProperties>'+
'<Cors>'+
'<CorsRule>'+
'<AllowedOrigins>*</AllowedOrigins>'+
'<AllowedMethods>GET,PUT</AllowedMethods>'+
'<MaxAgeInSeconds>500</MaxAgeInSeconds>'+
'<ExposedHeaders>x-ms-meta-data*,x-ms-meta-customheader</ExposedHeaders>'+
'<AllowedHeaders>x-ms-meta-target*,x-ms-meta-customheader</AllowedHeaders>'+
'</CorsRule>'+
'</Cors>'+
'<DefaultServiceVersion>2013-08-15</DefaultServiceVersion>'+
'</StorageServiceProperties>';
var url = MY_ACCOUNT_URL + '/?restype=service&comp=properties';
var canonicalizedResource = '/' + MY_ACCOUNT_NAME + '/?comp=properties';
var corsMD5 = crypto.createHash('md5' ).update(MY_CORS_XML).digest('base64');
var date = (new Date()).toUTCString();
var headers = {
'x-ms-version': '2013-08-15',
'x-ms-date': date,
'Host': MY_ACCOUNT_HOST,
'Content-Type': 'text/plain; charset=UTF-8',//Added this line
'Content-MD5': corsMD5,//Added this line
};
var canonicalizedHeaders = buildCanonicalizedHeaders( headers );
// THIS
var key = buildSharedKeyLite( 'PUT', corsMD5, 'text/plain; charset=UTF-8', canonicalizedHeaders, canonicalizedResource, accountKey);
// AND THIS, BOTH YIELD THE SAME SERVER RESPONSE
// var key = buildSharedKeyLite( 'PUT', "", "", canonicalizedHeaders, canonicalizedResource, accountKey);
headers['Authorization'] = 'SharedKeyLite ' + MY_ACCOUNT_NAME + ':' + key;
var options = {
url: url,
body: MY_CORS_XML,
headers: headers
};
console.log("url : " + url);
console.log("canonicalizedResource : " + canonicalizedResource);
console.log("canonicalizedHeaders : " + canonicalizedHeaders);
console.log("corsMD5 : " + corsMD5);
console.log("key : " + key);
console.log("options : " + JSON.stringify(options));
function onPropertiesSet(error, response, body) {
if (!error && response.statusCode == 202) {
console.log("CORS: OK");
}
else {
console.log("CORS: " + response.statusCode);
console.log("body : " + body);
}
}
request.put(options, onPropertiesSet); // require('request')
};
function buildCanonicalizedHeaders( headers ) {
var xmsHeaders = [];
var canHeaders = "";
for ( var name in headers ) {
if ( name.indexOf('x-ms-') == 0 ) {
xmsHeaders.push( name );
}
}
xmsHeaders.sort();
for ( var i = 0; i < xmsHeaders.length; i++ ) {
name = xmsHeaders[i];
canHeaders = canHeaders + name.toLowerCase().trim() + ':' + headers[name] + '\n';
}
return canHeaders;
}
function buildSharedKeyLite( verb, contentMD5, contentType, canonicalizedHeaders, canonicalizedResource, accountKey) {
var stringToSign = verb + "\n" +
contentMD5 + "\n" +
contentType + "\n" +
"" + "\n" + // date is to be empty because we use x-ms-date
canonicalizedHeaders +
canonicalizedResource;
// return crypto.createHmac('sha256', accountKey).update(encodeURIComponent(stringToSign)).digest('base64');
return crypto.createHmac('sha256', new Buffer(accountKey, 'base64')).update(stringToSign).digest('base64');
}

How to set Azure Blob Storage CORS properties using Node?

I have been following the blob service and authentication documentation in order to set the CORS properties on my azure blob storage account from my mobile service.
I can't figure out what I am doing wrong.
The server response is:
The MAC signature found in the HTTP request 'JI...Tk=' is not the same as any computed signature. Server used following string to sign: 'PUT
x-ms-date:Wed, 19 Feb 2014 07:24:06 GMT x-ms-version:2013-08-15
/apporotest/?comp=properties'
When I log the string to sign (not passing contentMD5 and content type) on my end, it turns out to be the same string. So I guess my function to build the shared key is wrong.
This should build : Base64(HMAC-SHA256(UTF8(StringToSign))):
function buildSharedKeyLite( verb, contentMD5, contentType, canonicalizedHeaders, canonicalizedResource ) {
var stringToSign = verb + "\n" +
contentMD5 + "\n" +
contentType + "\n" +
"" + "\n" + // date is to be empty because we use x-ms-date
canonicalizedHeaders +
canonicalizedResource;
return crypto.createHmac('sha256', self.accountKey).update(encodeURIComponent(stringToSign)).digest('base64');
}
What confuses me though is that the formerly mentioned documentation for the Shared Key Lite requires MD5 of the content as well as the content type to be set. However, the server response with the string to sign does not seem to expect these.
If the creation of the shared key lite is correct, then I assume I am not handling the creation of the MD-5 content correctly or the canonicalized headers:
function setCors( cors ) {
var url = MY_ACCOUNT_UTL + '/?restype=service&comp=properties';
var canonicalizedResource = '/' + MY_ACCOUNT_NAME + '/?comp=properties';
var corsMD5 = crypto.createHash('md5' ).update(MY_CORS_XML).digest('base64');
var date = (new Date()).toUTCString();
var headers = {
'x-ms-version': '2013-08-15',
'x-ms-date': date,
'Host': MY_ACCOUNT_HOST
};
var canonicalizedHeaders = buildCanonicalizedHeaders( headers );
// THIS
var key = buildSharedKeyLite( 'PUT', corsMD5, 'text/plain; charset=UTF-8', canonicalizedHeaders, canonicalizedResource );
// AND THIS, BOTH YIELD THE SAME SERVER RESPONSE
var key = buildSharedKeyLite( 'PUT', "", "", canonicalizedHeaders, canonicalizedResource );
headers['Authorization'] = 'SharedKeyLite ' + MY_ACCOUNT_NAME + ':' + key;
var options = {
url: url,
headers: headers
};
function onPropertiesSet(error, response, body) {
if (!error && response.statusCode == 202) {
console.log("CORS: OK");
}
else {
console.log("CORS: "+ response.statusCode);
console.log(body);
}
}
request.put(options, onPropertiesSet); // require('request')
}
function buildCanonicalizedHeaders( headers ) {
var xmsHeaders = [];
var canHeaders = "";
for ( var name in headers ) {
if ( name.indexOf('x-ms-') == 0 ) )
xmsHeaders.push( name );
}
}
xmsHeaders.sort();
for ( var i = 0; i < xmsHeaders.length; i++ ) {
name = xmsHeaders[i];
canHeaders = canHeaders + name.toLowerCase().trim() + ':' + headers[name] + '\n';
}
return canHeaders;
}
I am really thankful for any pointers.
I'm not 100% sure but I believe encodeURIComponent is creating problem for you. For example, look at the code below:
var a = "PUT\n\n\nFeb 2014 09:08:18 GMT\nx-ms-version:2013-08-15\n/cynapta/?comp=properties";
var b = encodeURIComponent(a);
console.log(a);
console.log("\n");
console.log(b);
and this is how a and b are displayed on my console:
Can you try by removing encodeURIComponent and just pass stringToSign directly for signature calculation?
Update
Looking at the source code here for signing the request and assuming you're passing storage account key as string, can you try the following in your buildSharedKeyLite function:
return crypto.createHmac('sha256', new Buffer(self.accountKey, 'base64')).update(stringToSign).digest('base64');

Resources