无法为Google服务帐户身份验证正确创建JWT

时间:2017-02-03 01:14:04

标签: javascript google-sheets google-api jwt google-authentication

我尝试在scriptr.io上创建一个脚本,创建JWT/JWS以发送到Google的令牌端点,以获得auth_token我的服务帐户。我正在使用CryptoJS library进行加密。我能够生成JWT的所有3个部分,但是这样做我做错了。我相信它与字符串的三个部分中的最后一部分(因此,签名部分)有关,但我可能是错的。

var cryptoJs = {};
cryptoJs['SHA256'] = require('CryptoJS/rollups/sha256.js').CryptoJS.SHA256

var pHeader = {"alg":"RS256","typ":"JWT"}
var sHeader = JSON.stringify(pHeader);
var encodedHeader = Base64EncodeUrl(btoa(sHeader));
console.log("encodedHeader: " + encodedHeader);

var now = new Date();
var oneHourExpiration = ((now.getTime()-now.getMilliseconds())/1000)+3000;//3000, not 3600 which is 1 hour

var pClaim = {};
pClaim.iss = "-------@---iam.gserviceaccount.com";
pClaim.scope = "https://www.googleapis.com/auth/spreadsheets";
pClaim.aud = "https://www.googleapis.com/oauth2/v3/token";
pClaim.exp = oneHourExpiration;
pClaim.iat = Math.floor(Date.now()/1000);
console.log("exp: " + pClaim.exp);
console.log("iat: " + pClaim.iat);

var sClaim = JSON.stringify(pClaim);
var encodedClaim = Base64EncodeUrl(btoa(sClaim));
console.log("encodedClaim: " + encodedClaim);

var byteArray = encodedHeader + "." + encodedClaim;
console.log("byteArray: " + byteArray);

var secret = "-----BEGIN PRIVATE KEY-----\n.....MIIE.....=\n-----END PRIVATE KEY-----\n";
var signature = cryptoJs.SHA256(byteArray, secret);
var encodedSignature = Base64EncodeUrl(btoa(signature));
console.log("Encoded Signature: " + encodedSignature);

var sJWS = byteArray + "." + encodedSignature;
console.log("JWT: " + sJWS);

function Base64EncodeUrl(str){
    return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
}

var http = require("http");
var requestObject = {
  "url": "https://www.googleapis.com/oauth2/v3/token",
  "method": "POST",
  "headers": {"Content-Type":"application/x-www-form-urlencoded"},
  "params": {"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer","assertion":sJWS}
}


var response = http.request(requestObject);
var responseBodyStr = response.body;
console.log(responseBodyStr);
var token = JSON.parse(responseBodyStr.access_token);
console.log(token);

当我使用JWT将请求发送到令牌端点时,我得到以下响应

{  
    "error": "invalid_grant",
    "error_description": "Invalid JWT Signature."
 }

知道我哪里出错了吗?有人可以帮我正确格式化JWT,这样我就能获得一个令牌吗?

1 个答案:

答案 0 :(得分:1)

使用的函数是做哈希,而不是数字签名

var signature = cryptoJs.SHA256(byteArray, secret);

不支持使用RSA私钥的数字签名。查看CryptoJS

主存储库中的注释
  

<强>不活动

     

CryptoJS是我在业余时间享受和工作的项目,但是   不幸的是,我的9比5没有给我留下尽可能多的空闲时间   以前。我仍然希望将来继续改进它,但我   不能说什么时候会的。如果您发现CryptoJS不符合   你的需求,然后我建议你尝试Forge。

我建议将代码移动到使用推荐的其他Javascript库。例如伪造支持RSA签名(https://github.com/digitalbazaar/forge#rsa

Google OAuth2服务器使用 RS256 。我提供了一个代码片段来转换密钥(我假设PEM格式),使用带有SHA256的RSA伪造和签名数据

  

Google OAuth 2.0授权服务器支持的唯一签名算法是使用SHA-256哈希算法的RSA。这在JWT标题的alg字段中表示为RS256。

// convert a PEM-formatted private key to a Forge private key
var privateKey = forge.pki.privateKeyFromPem(pem);

// sign data with a private key and output DigestInfo DER-encoded bytes (defaults to RSASSA PKCS#1 v1.5)
var md = forge.md.sha256.create();
md.update(byteArray, 'utf8');
var signature = privateKey.sign(md);

//convert signature to base64
var encodedSignature = Base64EncodeUrl(btoa(signature));