I am trying to create an SAS Token to communicate with Azure API Management Rest API using JavaScript (Express.js). But using that actually leads me to a 401 Unauthorized. I am using the following lines of code.
// setting one day expiry time
const expiryDate = new Date(Date.now() + 1000 * 60 * 60 * 24)
const expiryString = expiryDate.toISOString()
const identifier = process.env.AZURE_APIM_IDENTIFIER
const key = process.env.AZURE_APIM_SECRET_KEY ?? ""
const stringToSign = `${identifier}\n${expiryString}`
const signature = CryptoJS.HmacSHA256(stringToSign, key)
const encodedSignature = CryptoJS.enc.Base64.stringify(signature)
// SAS Token
const sasToken = `SharedAccessSignature uid=${identifier}&ex=${expiryString}&sn=${encodedSignature}`
The above snippet returns me something like this:
SharedAccessSignature uid=integration&ex=2021-04-21T10:48:04.402Z&sn=**O8KZAh9zVHw6Dmb03t1xlhTnrmP1B6i+5lbhQWe**= (Some characters hidden for security, but number of characters is real)
Note that there is only one trailing dash = in the above mentioned SAS token, whereas SAS Tokens in all examples and manually created SAS Token from API Management Portal have 2 dashes ==
Is there anything I am doing wrong?
Thanks in advance.
According to the document of SAS token for Azure APIM, we can see the sample is c# code:
The difference between the sample and your code is the c# sample uses HMACSHA512 but your code use HMAS256. So I think you also need to use HMACSHA512 in your nodejs. You can do it like:
var hash = crypto.createHmac('sha512', key);
You may also need to do hash.update(text); and hash.digest(), please refer to this document about it.
Thank you Hury Shen! I also figured out that we don't need crypto-js for (as we have to import an external library for that). Node has crypto as its native module and we can use that. The following JavaScript snippet works fine.
import crypto from "crypto"
const identifier = <YOUR_AZURE_APIM_IDENTIFIER>
const secretKey = <YOUR_AZURE_APIM_SECRET_KEY>
// setting token expiry time
const expiryDate = new Date(Date.now() + 1000 * 60 * 60 * 24 * 29)
const expiryString = expiryDate.toISOString().slice(0, -1) + "0000Z"
const dataToSign = `${identifier}\n${expiryString}`
// create signature
const signedData = crypto
.createHmac("sha512", secretKey)
.update(dataToSign)
.digest("base64")
// SAS Token
const accessToken = `SharedAccessSignature uid=${identifier}&ex=${expiryString}&sn=${signedData}`
Related
I am using node-redis. I have a cron job that updates my db and I have redis cache that caches the db response.
The problem I'm having is that my cron job runs everyday at 12am, however I can only set redis cache to expire in x seconds from now. Is there a way to make node-redis cache expire everyday at 12am exactly. Thanks.
Code:
const saveResult = await SET_CACHE_ASYNC('cacheData', response, 'EX', 15);
yes, you can use https://redis.io/commands/expireat command, if you use https://www.npmjs.com/package/redis package as redis driver, code will be like this
const redis = require('redis')
const client = redis.createClient();
const when = (Date.now()+24*60*60*1000) / 1000;
client.expireat('cacheData', when, function(error){....};
``
Recently I had the same problem with an application. My work around was creating a new timespan based on the time difference between my set and expiration time. Here is my code:
private TimeSpan GetTimeSpanUntilNextDay(int hour)
=> new DateTime(DateTime.Now.Date.AddDays(1).Ticks).AddHours(hour) - DateTime.Now;
Using the stack exchange lib for a 6 AM absolute expirition time the code looks like so:
public async Task<bool> SetEntranceAsync(string key, MYTYPE unserializedObject)
{
var db = _multiplexer.GetDatabase();
var jsonValue = JsonConvert.SerializeObject(unserializedObject);
return await db.StringSetAsync(key, jsonValue, GetTimeSpanUntilNextDay(6));
}
I used C# but you should be able to do the trick in any language.
Im trying to integrate 2Checkout on application that the front-end is build on Angular 11 and back end Node js, i managed to integrate IPN(Instant Payment Notification) of 2Checkout but i get a notification that says: ""
Our system detected that you have slow responding web hooks.
after checking documentation multiple times i saw that i need to return <EPAYMENT>20050303123434|7bf97ed39681027d0c45aa45e3ea98f0</EPAYMENT>
but still i get same response on dashboard and 2Checkout cannot verif
my code:
const bodyParse = parse(req.body);
let date = new Date();
let updatedDate =
date.getFullYear().toString() +
pad2(date.getMonth() + 1) +
pad2(date.getDate()) +
pad2(date.getHours()) +
pad2(date.getMinutes()) +
pad2(date.getSeconds());
let hashToReturn = hmac(
`${bodyParse['IPN_PID[]'].length}${bodyParse['IPN_PID[]']}${bodyParse['IPN_PNAME[]'].length}${bodyParse['IPN_PNAME']}${bodyParse['IPN_DATE'].length}${bodyParse['IPN_DATE']}${updatedDate.length}${updatedDate}`
);
console.log('hashToReturn', hashToReturn);
res.send(`Verified OK! <EPAYMENT>${updatedDate}|${hashToReturn}</>`)
function hmac(stringToHash: string) {
const secretKey = process.env.checkoutSecret;
const hmac = crypto.createHmac('MD5', secretKey);
return hmac.update(stringToHash).digest('hex');
}
I cant figure it out what I'm missing or what is wrong i tried to contact 2Checkout support but they just replied with code samples from PHP and parts from documentation.
I am trying to create an HMAC signature in Postman using a pre-request script. Without going too far into the details of implementation,
I have confirmed that my means for generating the signature is messed up. I can see what the expected result should be with a proof of concept example but I’m missing something somewhere and cannot tell if it is in the conversion. I’ve read around from other questions on SO that binary is the default provided by cryptojs internally and that simply calling for the hash is the equivalent of asking for the digest with conversions completed for you. Here is the code I’m trying to run in postman and the working implementation code as shown in nodeJS.
var CryptoJS = require("crypto-js");
const d = new Date();
const timestamp = d.getTime();
const postData = {};
postData.nonce = 100; //timestamp * 1000; //nanosecond
postman.setEnvironmentVariable('nonce', postData.nonce);
const secret = CryptoJS.enc.Base64.parse(pm.environment.get("apiSecret"));
const path = pm.globals.get("balanceMethod");
const message = CryptoJS.SHA256( encodeURI(postData.nonce + postData)) ; // ...
const hmacDigest = CryptoJS.HmacSHA512(path + message, secret);
postman.setEnvironmentVariable('API-Signature', CryptoJS.enc.Base64.stringify(hmacDigest));
console.log(CryptoJS.enc.Base64.stringify(hmacDigest));
Does this apply to my situation in that I’d need to convert my sha256 message into a bytes array in order to work?
Reference code for building implementation that does work with nodeJS:
const getMessageSignature = (path, request, secret, nonce) => {
const message = qs.stringify(request);
const secret_buffer = new Buffer(secret, 'base64');
const hash = new crypto.createHash('sha256');
const hmac = new crypto.createHmac('sha512', secret_buffer);
const hash_digest = hash.update(nonce + message).digest('binary');
const hmac_digest = hmac.update(path + hash_digest, 'binary').digest('base64');
return hmac_digest;
};
Same reference code for building implementation in python3:
req['nonce'] = 100 #int(1000*time.time())
postdata = urllib.parse.urlencode(req)
# Unicode-objects must be encoded before hashing
encoded = (str(req['nonce']) + postdata).encode()
message = urlpath.encode() + hashlib.sha256(encoded).digest()
signature = hmac.new(base64.b64decode(self.secret),
message, hashlib.sha512)
sigdigest = base64.b64encode(signature.digest())
The only post data I'm sending is the Nonce at this time and I've purposely set it to 100 to be able to replicate the result to fix the generated signature. Seems close but not matching result. The python and nodeJS do match expected results and work properly.
Check out the answer in this thread. It helped me with the my problem and may be what is happening in your case also. All it is necessary is break the input of the HMAC into two parts.
I've been searching for ways to restrict access to an API made for using a AWS Lambda function written on javascript.
I've found documentation on how to use AWS Signature S4, but I still do not understand it.
According to creating a signature, after applying the pseudocode I should get the signature to be placed on the header.
I've found the following code that addresses this point:
// Example of signature generator
var crypto = require("crypto-js");
function getSignatureKey(Crypto, key, dateStamp, regionName, serviceName) {
var kDate = Crypto.HmacSHA256(dateStamp, "AWS4" + key);
var kRegion = Crypto.HmacSHA256(regionName, kDate);
var kService = Crypto.HmacSHA256(serviceName, kRegion);
var kSigning = Crypto.HmacSHA256("aws4_request", kService);
return kSigning;
}
console.log(getSignatureKey(crypto,'secretkey','date','us-east-2','iam'));
Here comes my first question, I do not know what should be the output of getSignatureKey()? This is because on the documentation it is a very long string, while the output I got was {words:[x,x,x,x,x,x,x,x],sigBytes: 32},where the x are random numbers.
Moreover, after getting the signature and filling the header for the request with the "authorization" field and others, how do I filter unproper requests? Do I have to create a policy for the AWS API so it only allows signed requests? Here I guess I should follow Signing Requests.
Thanks!
Here is the simple implementation of Signed URL's. aws-cloudfront-sign package offers simpler implementation.
var cfsign = require('aws-cloudfront-sign');
var signingParams = {
keypairId: process.env.PUBLIC_KEY,
privateKeyString: process.env.PRIVATE_KEY,
// Optional - this can be used as an alternative to privateKeyString
privateKeyPath: '/path/to/private/key',
expireTime: 1426625464599
}
// Generating a signed URL
var signedUrl = cfsign.getSignedUrl(
'http://example.cloudfront.net/path/to/s3/object',
signingParams
);
https://aws.amazon.com/blogs/developer/creating-amazon-cloudfront-signed-urls-in-node-js/
Purpose of SignedURL is to serve Private Contents.
More details at,
http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html
Hope it helps.
I'm using Gigya Comment Notification service in my node app and trying generate a valid signature. I have followed this documentation, but my code generate wrong hash.
This is my code:
var crypto = require('crypto');
var params = [the notification object from the request];
var eventData = JSON.stringify(params.eventData);
var text = params.event + '_'
+ eventData + '_'
+ params.nonce + '_'
+ params.timestamp;
var secret = new Buffer('Qmxxxxxxxxxxxxx...xxxxxxw=', 'base64');
var hash = crypto.createHmac('sha1', secret).update(text).digest('base64');
if (hash !== params.signature) {
console.log('Not ok')
} else{
console.log('Ok')
}
I think the signature base (text variable) construction may not valid.
This is what my text variable contains (with fake datas):
newComment_[{"categoryID":"category","streamID":"stream","commentID":"123","comment":{"ID":"123","etc":"foobar","timestamp":1447078842653,"threadTimestamp":1447078842653,"status":"published"}}]_aaaaaaaa-bbbb-cccc-dddd-ffffffffffff_1447078842
How can I generate the right signature?
Your signature base construction looks correct, although a nonce doesn't generally have any dashes in it.
The most common reason for a generating an incorrect signature is using the incorrect Secret Key.
Your partner's Secret Key is provided in BASE64 encoding, at the bottom of the Sites Table in the Dashboard section in Gigya website (please make sure that you are logged in to Gigya's website and that you have completed the Gigya's Site Setup process). Please make sure that you are using the partner's Secret Key and not your user's Secret Key.