I am a newer Apps Scripts user who has been stuck on this problem for some time. I am trying to connect my company's reporting feature to a Google Sheet so I can create reporting dashboards for our users.
I have a .jsNode code snippet which I am able to successfully run outside of the Apps Scripts editor, but I am having issues translating it to Apps Scripts.
My goal is to be able to generate an Authentication Token which will be used in a header within a POST request. I will then use this to get a specific URL that I can pull data from (it will be a .csv file. I already feel I can accomplish this using another script)
Below is the .NodeJS code snippet:
const crypto = require('crypto');
module.exports.init = function () {
let prefix = 'abc-admin-v1'
let businessId = 'XXXX'
let publicAppKeyId = 'XXXX'
let secretKey = 'XXXX'
let unixTimestamp = Math.floor(Date.now() / 1000)
let payload = `${prefix}${businessId}${unixTimestamp}`
let rawKey = Buffer.from(secretKey, 'base64')
let signature = crypto.createHmac('sha256', rawKey).update(payload, 'utf8').digest('base64')
let token = `${signature}${payload}`
let httpBasicPayload = `${publicAppKeyId}:${token}`
let httpBasicCredentials = Buffer.from(httpBasicPayload, 'utf8').toString('base64')
console.log(httpBasicCredentials);
};
Below will be what I have in Apps Scripts:
function apiInfo() {
var secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
var apiKey = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
var locationID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
var prefix = "abc-admin-v1";
var timeStamp = Math.round((new Date()).getTime() / 1000);
var timeStampReal = JSON.stringify(timeStamp);
var tokenPayload = prefix + businessID + timeStampReal;
var enCodeTokenPayload = Utilities.base64Encode(tokenPayload);
var deCodeTokenPayload = Utilities.base64Decode(enCodeTokenPayload);
var tokenPayloadReal = JSON.stringify(tokenPayload);
var rawKey = Utilities.base64Decode(secret);
var rawMac = Utilities.computeHmacSha256Signature(tokenPayloadReal, rawKey);
var signature = Utilities.base64Encode(rawMac);
var token = signature + tokenPayload;
var httpBasicPayload = apiKey + ":" + token;
var httpBasicCredentials = Utilities.base64Encode(httpBasicPayload);
When testing using the current GAS code, it produces an invalid Auth Token.
When I run the Node.js version outside of GAS, it produces a valid Auth Token. I am looking to find out what is currently wrong with my code that is producing an invalid auth token. I would like to keep the project within GAS if possible.
I believe your goal is as follows.
You want to convert your showing Node.js script to Google Apps Script. Namely, you want to retrieve the same values of httpBasicCredentials between your Node.js script and Google Apps Script.
const crypto = require('crypto');
module.exports.init = function () {
let prefix = 'abc-admin-v1'
let businessId = 'XXXX'
let publicAppKeyId = 'XXXX'
let secretKey = 'XXXX'
let unixTimestamp = Math.floor(Date.now() / 1000)
let payload = `${prefix}${businessId}${unixTimestamp}`
let rawKey = Buffer.from(secretKey, 'base64')
let signature = crypto.createHmac('sha256', rawKey).update(payload, 'utf8').digest('base64')
let token = `${signature}${payload}`
let httpBasicPayload = `${publicAppKeyId}:${token}`
let httpBasicCredentials = Buffer.from(httpBasicPayload, 'utf8').toString('base64')
console.log(httpBasicCredentials);
};
When I saw your showing Google Apps Script, unfortunately, there are several undeclared variable and unused variables. By this, in this answer, I would like to propose to directly converting your showing Node.js to Google Apps Script.
Sample script:
function myFunction() {
let prefix = 'abc-admin-v1'
let businessId = 'XXXX'
let publicAppKeyId = 'XXXX'
let secretKey = 'XXXX'
let unixTimestamp = Math.floor(Date.now() / 1000);
let payload = `${prefix}${businessId}${unixTimestamp}`;
let payloadBytes = Utilities.newBlob(payload).getBytes();
let rawKey = Utilities.base64Decode(secretKey);
let signature = Utilities.base64Encode(Utilities.computeHmacSha256Signature(payloadBytes, rawKey));
let token = `${signature}${payload}`;
let httpBasicPayload = `${publicAppKeyId}:${token}`;
let httpBasicCredentials = Utilities.base64Encode(httpBasicPayload);
console.log(httpBasicCredentials);
}
Testing:
In the above scripts, when the value of unixTimestamp is 1661403828, both your Node.js and my sample Google Apps Script are the same values as follows.
WFhYWDpGYXBwRk9kaFoxdDVQNGVkV2JJbFIvR1RiMkEyZ0hwYThld05BYjVDVkxnPWFiYy1hZG1pbi12MVhYWFgxNjYxNDAzODI4
Note:
From your question, in this answer, your Node.js is converted to Google Apps Script. Please be careful about this.
References:
base64Encode(data)
computeHmacSha256Signature(value, key)
Related
I am trying to sign a build & sign transaction then create a hex to broadcast the transaction on TRON Network.
I have successfully done this but when i broadcast this transaction i am getting "TRON TAPOS_ERROR" error.
I have searched and got the reason that i have to include last block number and hash.
check this : https://github.com/tronprotocol/java-tron/issues/857
But I don't know how to do this.
I have tried this code :
const CryptoUtils = require("#tronscan/client/src/utils/crypto");
const TransactionUtils = require("#tronscan/client/src/utils/transactionBuilder");
async function transferContractTx() {
const fromAddress = "FROM_ADDRESS";
const toAddress = "TO_ADDRESS";
const privateKey = "MY_PRIVATE_KEY";
const token = "TRX";
const amount = 1000000;
let transaction = TransactionUtils.buildTransferTransaction(token, fromAddress, toAddress, amount);
console.log(JSON.stringify(transaction));
let signedTransaction = CryptoUtils.signTransaction(privateKey, transaction);
console.log(signedTransaction);
}
transferContractTx();
i want to us Key Vault key to create JWT token and then validate it.
Im using this code:
public static async Task<string> SignJwt()
{
var tokenHandler = new JwtSecurityTokenHandler();
var signinKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("this is my custom Secret key for authentication"));
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("id", "1") }),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(signinKey, SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
and it works fine. I was googling a lot and found this snippet for SigningCredentials using Identity extension nuget:
new SigningCredentials(new KeyVaultSecurityKey("https://myvault.vault.azure.net/keys/mykey/keyid", new KeyVaultSecurityKey.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)), "RS256")
{
CryptoProviderFactory = new CryptoProviderFactory() { CustomCryptoProvider = new KeyVaultCryptoProvider() }
});
But it is not clear for me, what really AuthenticationCallback is and how to implement that and if i will be able to use that in Azure in web app or azure function?
Firstly, a JWT token consists of 3 parts (Header, Payload and Signature) and all those 3 parts are Base64UriEncoded.
To get the Signature you need to generate header and payload, then combine them by dot.**
Below is the sample code to verify JWT using Azure kay Vault.
const key = await this.keyClient.getKey(this.KEY_NAME);
const cryptClient = new CryptographyClient(key, new DefaultAzureCredential());
const util =require('util')
const base64 = require('base64url');
const JWT=""
const jwtHeader = JWT.split('.')[0];
const jwtPayload = JWT.split('.')[1];
const jwtSignature = JWT.split('.')[2];
const signature = base64.toBuffer(jwtSignature)
const data = util.format('%s.%s', jwtHeader, jwtPayload);
const hash = crypto.createHash('sha256');
const digest = hash.update(data).digest()
const verified =await cryptClient.verify("RS256",digest,signature)
Here are few SO threads with related discussions. SO1, SO2 and SO3
I am using Microsoft Azure API Management service and want to use the REST API service. In creating my SAS token, which is needed otherwise the API call doesn't authorize, I'm having difficulty forming a proper token. Microsoft's webpage about this SAS token for API Management only shows an example in C#. I want to know how to form an SAS token in Node.js, which is not shown. Below is my code that was working last week, but is not now for some unknown reason. The error I get is: 401 Authorization error, token invalid
If someone can help me formulate this token, I would appreciate it.
This is Microsoft's webpage regarding this authentication token: https://learn.microsoft.com/en-us/rest/api/apimanagement/apimanagementrest/azure-api-management-rest-api-authentication
Here's my code:
const crypto = require('crypto');
const util = require('util');
const sign = () => {
const id = ${process.env.id}
const key = `${process.env.SASKey}`;
const date = new Date();
const newDate = new Date(date.setTime(date.getTime() + 8 * 86400000));
const expiry = `${newDate.getFullYear()}${
newDate.getMonth() < 10
? '' + newDate.getMonth() + 1
: newDate.getMonth() + 1
}${newDate.getDate()}${newDate.getHours()}${
newDate.getMinutes() < 10
? '0' + newDate.getMinutes()
: newDate.getMinutes()
}`;
const dataToSignString = '%s\n%s';
const dataToSign = util.format(dataToSignString, ${id}, expiry);
const hash = crypto
.createHmac('sha512', key)
.update(dataToSign)
.digest('base64');
const encodedToken = `SharedAccessSignature ${id}&${expiry}&${hash}`;
console.log(encodedToken);
return encodedToken;
};
Try the code:
protected getAPIManagementSAS(){
let utf8 = require("utf8")
let crypto= require("crypto")
let identifier = process.env.API_IDENTIFIER;
let key = process.env.API_KEY;
var now = new Date;
var utcDate = new Date(now.getUTCFullYear(),now.getUTCMonth(), now.getUTCDate() , now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds());
let expiry = addMinutes(utcDate,1,"yyyy-MM-ddThh:mm:ss") + '.0000000Z'
var dataToSign = identifier + "\n" + expiry;
var signatureUTF8 = utf8.encode(key);
var signature = crypto.createHmac('sha512', signatureUTF8).update(dataToSign).digest('base64');
var encodedToken = `SharedAccessSignature uid=${identifier}&ex=${expiry}&sn=${signature}`;
return encodedToken
}
For more information, see here.
After a million tries, it seems like the only format acceptable right now is:
SharedAccessSignature uid=${identifier}&ex=${expiry}&sn=${signature}
If you are using the other format that has the "integration" parameter, that's a hit or a miss, mostly miss though. Set the uid as "integration" if that's your identifier and follow the above format as it works.
I have this kind of api example and I want to use this in nodejs.
/*
https://code.google.com/archive/p/crypto-js/
https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/crypto-js/CryptoJS%20v3.1.2.zip
*/
<script type="text/javascript" src="./CryptoJS/rollups/hmac-sha256.js"></script>
<script type="text/javascript" src="./CryptoJS/components/enc-base64.js"></script>
function makeSignature() {
var space = " "; // one space
var newLine = "\n"; // new line
var method = "GET"; // method
var url = "/photos/puppy.jpg?query1=&query2"; // url (include query string)
var timestamp = "{timestamp}"; // current timestamp (epoch)
var accessKey = "{accessKey}"; // access key id (from portal or Sub Account)
var secretKey = "{secretKey}"; // secret key (from portal or Sub Account)
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, secretKey);
hmac.update(method);
hmac.update(space);
hmac.update(url);
hmac.update(newLine);
hmac.update(timestamp);
hmac.update(newLine);
hmac.update(accessKey);
var hash = hmac.finalize();
return hash.toString(CryptoJS.enc.Base64);
}
But the problem is when I use this in Nodejs, I don't know how to require those CryptoJS.
For example, I downloaded CryptoJS file by google. and it is reading by require.
Even though it is read, I don't know which should I read correctly.
Could you help how to solve this problem?
const CryptoJS = require('./CryptoJS v3.1.2/components/enc-base64');
In NodeJS (latest version), you don't even need to download an external library or install from NPM.
Nodejs has crypto built-in library.
const crypto = require('crypto');
var space = " ";
var newLine = "\n";
var method = "GET";
var url = "/photos/puppy.jpg?query1=&query2";
var timestamp = "{timestamp}";
var accessKey = "{accessKey}";
var secretKey = "{secretKey}";
const hash = crypto.createHmac('sha256', secretKey)
.update(method)
.update(space)
.update(url)
.update(newLine)
.update(timestamp)
.update(newLine)
.update(accessKey)
.digest('hex');
console.log(hash);
First of all I dont know why you download file from google? There is very useful npm library. find it here and use it. https://www.npmjs.com/package/crypto-js
I've been following the AWS example on how to generate a V4 HMAC signature. I've done this successfully in Java but I'm trying to get it to work in Node/JavaScript. When I use my code I generate all the correct intermediary keys in their 1st example below but on the next example when given the test StringToSign the same code that generated the correct intermediary keys fails to generate the supposed correct signature.
Correct Intermediary Keys:
secretkey = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20120215'
regionName = 'us-east-1'
serviceName = 'iam'
kSecret = '41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559'
kDate = '969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d'
kRegion = '69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c'
kService = 'f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa'
kSigning = 'f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d'
http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html.
Fails With the Following Input
secretkey = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20151229'
regionName = 'us-east-1'
serviceName = 's3'
StringToSign
eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJzaWd2NGV4YW1wbGVidWNrZXQifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwNCiAgICB7ImFjbCI6ICJwdWJsaWMtcmVhZCJ9LA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL3NpZ3Y0ZXhhbXBsZWJ1Y2tldC5zMy5hbWF6b25hd3MuY29tL3N1Y2Nlc3NmdWxfdXBsb2FkLmh0bWwifSwNCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sDQogICAgeyJ4LWFtei1tZXRhLXV1aWQiOiAiMTQzNjUxMjM2NTEyNzQifSwNCiAgICB7IngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24iOiAiQUVTMjU2In0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sDQoNCiAgICB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlPU0ZPRE5ON0VYQU1QTEUvMjAxNTEyMjkvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LA0KICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwNCiAgICB7IngtYW16LWRhdGUiOiAiMjAxNTEyMjlUMDAwMDAwWiIgfQ0KICBdDQp9
Correct Signature: 46503978d3596de22955b4b18d6dfb1d54e8c5958727d5bdcd02cc1119c60fc9
My Signature: e7318f0bfd7d86fb9ba81c314f62192ee2baf7273792ef01ffafeb430fc2fb68
http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
My Code
var crypto = require("crypto-js")
module.exports.getSignatureKey = function(key, dateStamp, regionName, serviceName) {
var kSecret = "AWS4" + key
var kDate = crypto.HmacSHA256(dateStamp, kSecret)
var kRegion = crypto.HmacSHA256(regionName, kDate)
var kService = crypto.HmacSHA256(serviceName, kRegion)
var kSigning = crypto.HmacSHA256("aws4_request", kService)
return kSigning;
}
module.exports.sign = function(signatureKey,stringToSign) {
var unencodedSignature = crypto.HmacSHA256(stringToSign,signatureKey)
return unencodedSignature
}
module.exports.getSignature = function(stringToSign,secretKey,dateStamp,regionName, serviceName) {
var signingKey = this.getSignatureKey(secretKey,dateStamp,regionName,serviceName)
return this.sign(signingKey,stringToSign)
}
The AWS example in the second link has the wrong signature. Using my solution I am able to successfully upload to s3.
Something else to consider is that the crypto-js node library outputs the signatures already in hex. Theres no need to do a manual conversion yourself as you would if you we in Java using the example code they provide.