使用 SuiteScript 2.x 为 DocuSign API 集成创建 JWT

Creating JWT Using SuiteScript 2.x for DocuSign API Integration

我正在更新 DocuSign 的 NetSuite 集成,以将所有脚本转换为 SuiteScript 2.0 并更新身份验证以使用 JWT 授权方法,因为目前的方法看起来将在几周内被新应用程序弃用,并完全在明年的某个时候。不过,我确实有几个问题无法回答。

首先,在为 body 设置 IAT 和 EXP 值时,我是否根据当前服务器时间获取此值(这取决于帐户数据中心恰好所在的时区),还是应该以获取 UTC 时间为基础?在这件事上,文档对我来说似乎不够清楚。

此外,我不清楚如何着手创建签名。除了上述问题之外,检索和准备必要的数据不是问题。但是,与我在创建签名时看到的示例相比,根据我在 2.0 模块中看到的可用内容安排要编码的数据似乎不太一致。例如,这是我所看到的近似值:

var header = { typ : "JWT" , alg : "RS256" };
var body = { iss : "abc123" , sub : "def456" , iat : 123456789 , exp : 123456789 + (60 * 60) , scope : "signature" };
var encHeader = base64UrlEncode(JSON.stringify(header));
var encBody = base64UrlEncode(JSON.stringify(body));

// the key pairs are stored elsewhere, and the functions represent a way to retrieve the key contents
var publickKey = retrievePublicKey();
var privateKey = retrievePrivateKey();
var signature = RS256Encode(
    encHeader + "." + encBody ,
    publicKey ,
    privateKey
);

这些示例显示了不同的加密函数,它们都以与上述相同的方式接收三个参数。然而,当查看本机 SuiteScript 2.0 模块中可用的内容时,我没有看到任何可比性。我看到加密模块(它似乎被拉入其他模块)确实有能力执行所需的 RSA SHA256 编码,但不太确定如何确保所有部分都正确地组合在一起。

我在 https://jwt.io/ 查看了用于创建 JWT 的 JavaScript 库,但我没有尝试将节点模块合并到 SuiteScript 项目中的经验,如果可能的话。

那么,有没有一种方法可以在 SuiteScript 中本地构建 JWT,或者我是否必须找到一种方法才能在脚本中引用节点模块?

-- 编辑--

好的,看来我可以使用“异步模块定义”(AMD) 通过创建 JSON 配置文件并将以下内容添加到脚本文件的 JSDoc [=53] 来将模块导入脚本=]:

*@NAmdConfig  /path/to/myModule.json

我想因为我需要它是一个相对路径,因为这个项目将分发给其他帐户,如果 JSON 配置文件在同一目录中,这样的事情应该有效:

*@NAmdConfig ./nodeModules.json

但我正忙于确定如何设置 JSON 配置。我似乎无法找到任何真正有助于确定如何正确构建它的东西。最大的问题是确定模块是 AMD 还是 non-AMD,如果 non-AMD 是否尝试导入 CJS 部分或 ESM 部分下的脚本。

对于任何其他参考,我使用 NPM 安装 jose 模块,因为它与 JavaScript/Node 兼容并且具有我需要的加密方法。

npm install jose

现在,它在源项目中的路径如下所示:

SuiteScripts/Project_Name/lib/node_modules/jose

将引用该模块的脚本文件位于此路径下:

SuiteScripts/Project_Name/lib
/**
 *@NApiVersion 2.x
 */

 define(['N/encode', 'N/crypto/certificate'], function(encode, cert){

   function signIt(payload, ttl){


      if(typeof payload.exp == 'undefined'){
         var secondsSinceEpoch = Math.round(Date.now() / 1000);
         var expAt = secondsSinceEpoch + (ttl || 60); 
         payload["exp"] = expAt;
         payload["iat"] = secondsSinceEpoch;
      }

      log.debug({
         title:'payload',
         details: JSON.stringify(payload)});

      var header = encode.convert({
         string: JSON.stringify({
            type:'JWT',
            alg:'RS256'
         }),
         inputEncoding: encode.Encoding.UTF_8,
         outputEncoding: encode.Encoding.BASE_64_URL_SAFE
      }).replace(/=+$/, '');

      var body = encode.convert({
         string: JSON.stringify(payload),
         inputEncoding: encode.Encoding.UTF_8,
         outputEncoding: encode.Encoding.BASE_64_URL_SAFE}).replace(/=+$/, '');

      var signer = cert.createSigner({
         certId:'custcertificate_sample', //from Setup -> Company -> Certificates
         algorithm: cert.HashAlg.SHA256
      });

      signer.update(header +'.'+ body);

      var sig = signer.sign({
         outputEncoding:encode.Encoding.BASE_64_URL_SAFE
      }).replace(/=+$/, '');

      return [header, body, sig].join('.');
   }

   return {
      signIt: signIt
   }
});

上传的密钥生成如下:

openssl genrsa -out private.pem 4096
openssl rsa -in private.pem -out public.pem -pubout
openssl req -key private.pem -new -x509 -days 3650 -subj "/C=CA/ST=Courtenay/O=Rule of Tech/OU=Information unit/CN=jwt.kotn.com" -out cert.pem

然后我只是使用文本编辑器连接来自 cert.pem 和 private.pem 的证书和私钥:

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----