繁体   English   中英

Nodejs 发布请求以获取服务帐户的 oauth2 令牌

[英]Nodejs post request to get oauth2 token for a service account

我正在尝试使用 nodejs 为我的服务帐户获取一个 oauth2 令牌。 我正在关注此处找到的文档:

https://developers.google.com/identity/protocols/OAuth2ServiceAccount#makingrequest

虽然没有节点示例,但我查看了 HTTP/Rest 文档以大致了解在请求令牌时它期望什么。 然而,我得到的回应是:

JWT 签名无效。

作为计算签名时的一般概述,您采用 header 的 base64url 编码值并声明 hash 使用 sha256 和来自谷歌开发者控制台的私钥,然后 base64url 对该值进行编码。

所以我的 header 是:

    var header = {"alg":"RS256","typ":"JWT"}
    var encodedHeader = base64url(new Buffer(header).toString('utf8'));

上面的文档甚至列出了这个值在 base64url 编码后的值:

    eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9

当我 output 我的 encodedHeader 值时:

    eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9

哦,它匹配得很好。所以其他值不应该有 64burl 编码问题。 接下来是索赔:

    var claim ={"aud":"https://www.googleapis.com/oauth2/v4/token","scope":"https://www.googleapis.com/auth/gmail.send","iss":"*****censoredvalue*****gserviceaccount.com","exp":1505399833,"iat":1505396233}

它经过相同的过程并被编码。(出于安全原因无法显示)。

    var encodedClaim = base64url(new Buffer(claim).toString('utf8'));

目前,我正在使用第三方站点计算签名以用于测试目的,但输入是 encodedHeader.encodedClaim + Private Key(值为:-----BEGIN PRIVATE KEY ....)并使用 SHA256。 然后我将使用 output 和 b64url 对其进行编码。

我现在有一个 JWT 是 encodedHeader.encodedClaim.encodedSignature

    var encoded_jwt = encodedHeader + '.' + encodedClaim + '.' + encodedSignature;

我将使用以下内容向令牌端点发出请求:

    // Set the headers
    var headers = {
       'HTTP-Version': 'HTTP/1.1',
       'Content-Type': 'application/x-www-form-urlencoded'
    }

    var bodyOptions = { 'grant_type': "urn:ietf:params:oauth:grant-type:jwt-bearer", assertion: encoded_jwt }

    // Configure the request
    var options = {
        url: 'https://www.googleapis.com/oauth2/v4/token',
        method: 'POST',
        headers: headers,
        form: bodyOptions,
        json: true
     }

    // Start the request
   request(options, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            // Print out the response body
            console.log(body)
        }
        else{
            context.log(error);
            context.log(response);
        }
    });

这是我得到错误响应的地方,说我有一个无效的签名。

关于为什么它是无效签名的任何想法。 我还尝试了几个签名生成站点以确保它不是那个特定的站点,但它始终是相同的错误。

要计算JWS,可以使用如下所示的加密模块

const privateKey = fs.readFileSync('private.pem', 'utf-8');

const signer = crypto.createSign('sha256');
signer.update(data);
const signature = signer.sign(privateKey, 'base64');

data encodedHeader + '.' + encodedClaimencodedHeader + '.' + encodedClaim encodedHeader + '.' + encodedClaim

来自google-oauth-library测试用例的完整示例:

"use strict";

const crypto = require('crypto');
const fs = require('fs');

const publicKey = fs.readFileSync('public.pem', 'utf-8');
const privateKey = fs.readFileSync('private.pem', 'utf-8');

const maxLifetimeSecs = 86400;
const now = new Date().getTime() / 1000;
const expiry = now + (maxLifetimeSecs / 2);

const idToken = '{' +
    '"iss":"testissuer",' +
    '"aud":"testaudience",' +
    '"azp":"testauthorisedparty",' +
    '"email_verified":"true",' +
    '"id":"123456789",' +
    '"sub":"123456789",' +
    '"email":"test@test.com",' +
    '"iat":' + now + ',' +
    '"exp":' + expiry + '}';

const envelope = '{' +
    '"kid":"keyid",' +
    '"alg":"RS256"' +
    '}';

let data = new Buffer(envelope).toString('base64') + '.' +
    new Buffer(idToken).toString('base64');

const signer = crypto.createSign('sha256');
signer.update(data);
const signature = signer.sign(privateKey, 'base64');

data += '.' + signature;

console.log(data);

以下使用“crypto”模块的代码片段非常适合我使用 RSA-SHA256 签名算法和私钥生成 JWS。 最后使用它来创建 OAuth 服务器所需的 JWT。

import { createVerify, createSign } from 'crypto';

const privateKey = 'read from a file or key store';
const publicKey = 'read from a file or key store'; // only if signature verification is required

// header and claim - basis of the input that is going to be signed by the private key
const header = { alg: 'RS256', typ: 'JWT' };
const claim = {
  iss: 'MY_ISSUER',
  scope: '',
  aud: 'https://example.com/path/to/oAuth/token.json',
  iat: Date.now(),
  exp: Date.now() + 60 * 60 * 1000, // setting 1 hr
};

// create base64 of the header and claim
const base64Header = Buffer.from(JSON.stringify(header), 'utf8').toString(
  'base64'
);
const base64Claim = Buffer.from(JSON.stringify(claim), 'utf8').toString(
  'base64'
);

// create the input from the encoded header and claim; 
// this is the input that is going to be signed by the secret key
const inputForSignature = `${base64Header}.${base64Claim}`;

// create the signing object using 'RSA-SHA256' algo,
// and add the input to it
const sign = createSign('RSA-SHA256');
sign.update(inputForSignature, 'utf8');
sign.end();

// sign the input using your private key and get the signature in base64 format
const base64Signature = sign.sign(privateKey, 'base64');

// optionally, if you want to verify the signature using the public key
const verify = createVerify('RSA-SHA256');
verify.write(inputForSignature);
verify.end();
if(verify.verify(publicKey, base64Signature, 'base64')) {
  console.log('signature verified');
} else {
  console.log('signature could not be verified');
}

// form the JWT by concatenating the above encoded parameters separated by dot (".") 
const JWT = `${base64Header}.${base64Claim}.${base64Signature}`;
console.log(JWT); // you are looking for

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM