Google OAuth2 服务帐户 HTTP/REST 身份验证

2023-12-31

我正在尝试使用下面的文档发出访问令牌请求:https://developers.google.com/identity/protocols/OAuth2ServiceAccount https://developers.google.com/identity/protocols/OAuth2ServiceAccount

我正在利用 jsrsasign 库来生成我的 JWT

为了清楚起见,我将代码部署到 Parse.com 云代码,除了 Google 的响应失败之外,一切都执行良好。

jsrsasign [http://kjur.github.io/jsrsasign/] http://kjur.github.io/jsrsasign/%5D https://kjur.github.io/jsrsasign/api/symbols/KJUR.jws.JWS.html#.sign https://kjur.github.io/jsrsasign/api/symbols/KJUR.jws.JWS.html#.sign

Parse.Cloud.define("testBase", function(request, response) {

  var createJWT = function(){

    var creds = {
      private_key_id: "532ca15e518a0<foobar>74dd81d48a9cb24",
      private_key: "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcw<foobar>QCzxcu5ae3l7JXT\nZzI2kHA3lYay/2DIcC4KXqQWCMejQacRlFROftfnqRrf9qmEewH/0TMSMlOFt0G6\n9hjznhOHu3rxZcAxuK5bh7UnmoUWYksdtb+6VgCGF9Z5piTASrLxssILAUqY6EjT\nHKp3IQk6aqMqe6NhymCS/o0K9NvGA98znpv28ilD9dd5HXhfTdeGm7PDkZGIXIbR\nG+sQN8+tW46N1PaYnoz8iNGfvGk2Y04WDC2HJ590z4DAk41jbcWtnZnr/UyIJzTq\nTBjCWwAcF0qwSabf/mlWSf2S7DeCZKYNveMSN7F6meI8uYshuVoNqd95u/KGfQ4q\ns+wMdF3tAgMBAAECggEAcQ2MhnelUhisSBv3qfS/fVUdNmf/d02ExqSpz+mJkpNw\n+08qjYqbQGZKLloyVMv+f+ARm/nmKIsMXQTywBHC+nLeZ/yzFxGrJIh9VgCIfYEm\n9/IaNpZrEejfyfS/2+WeDv15pe4T+YDqe0jlsrEl2oTBQ7ApGRBqF0bZb/B4XVd3\ngZ6kya+UL5j+PSgNcaGABUfj7pDXZIRmVnWJxXSYhvvbD+SrXIhBMS3wXZ+vka1J\nkW/bwhzayu9/nI24WN7pALxf6/zB3Ewyoj5n1pnxbkvBMcK+PBiX9yAPvfH5cGQF\nQZx86L11maYpWHxufrbclow16qZHP41O+1eePGbIgQKBgQDmOY04z02RIMX32I7k\nbtokmG8qDnR8iu8dkLSRU4Ib7ZPzIBpjg9neaRib9A2fPVjyuxjvsUbob11BuklZ\nGCMu9SFV8w4LpUQ3clL+kBiGncuSmBfZWbj7uqLuzsNeeu3pihVTlkeWeagTAR36\nhi8K4IVQi5SmPF2dPiw9A8oXkQKBgQDH5j1smCFu9d+F2HIwDFXsW1wlyWhtcgfV\n70uCXdImnU25pJDARLX8vqaZp0KHIPmXLgUV/sU3oAX9NRdgV56bJTo7vwO3DATM\ntK14h7GZCKSYniOqX+3FdweNyn89qlHeAkZdvCZhGX5rOVXtlhpey7Eu8fQnPs1S\nbxd1EXRKnQKBgH+m1Yj0WLvpghskdkZuuIGmC600Cp6rol2wSI5z0SaPGoOp/zfC\neeD6QOzn602qBFHCL9dnYjuq0/iHw/ekjI2S2YMAm38Vibd8qkv/tbmecKu9rSuU\nth7No13qQyV138ioCZ8pKlRi7DBtZCPultLfHsxEOI3b1sRDHuBN45YhAoGAGbKe\niNxRx/rxvjoiC806KoVgJjdrJk63dSgrE9pNzssAF/Jw7Van8pLrxer7oXV6wJWY\n78ftwIXg3zk5BRieeiFiCBY5OwnfgBVmC42eJic3SatiuF9WqMDxhqfWja3ckmbG\ndvxeDrOBTfVz93QJddBHudo+4eCv8n33jQQuZ/0CgYEAsYXmQWOUndqBaZgho3ZV\njrRFmwiwiqJ1hqJdflBXbKlTQOpqea8QoQOQqyeMVQ1X7x3rDcHbhFSbd65GJT5j\n65B/OXrBpIBhb5u0/x6ytJhlM9sPRIL+G/m5QYnBY7dcQo6jlKxTUKHPEV/mwT2m\nt/ZxkAmz/9DKWFKtDc4ZshI\u003d\n-----END PRIVATE KEY-----\n",
      client_email: "<foobar>-t18b3hrk[email protected] /cdn-cgi/l/email-protection",
      client_id: "<foobar>-t18b3hrkab6urireblm8kb4kt45c92a2.apps.googleusercontent.com",
      type: "service_account"
    };

    var header = {
      alg:"RS256",
      typ:"JWT"
    };

    var data = {
      iss: creds.client_email,
      scope:"https://www.googleapis.com/auth/analytics.readonly",
      aud:"https://www.googleapis.com/oauth2/v3/token",
      exp: KJUR.jws.IntDate.get('now + 1hour'),
      iat: KJUR.jws.IntDate.get('now')
    };

    console.log("Preparing to generate RS256 JWT");
    var sJWT = KJUR.jws.JWS.sign("RS256", JSON.stringify(header), JSON.stringify(data), creds.private_key);
    console.log("RS256 JWT generation complete:");

    console.log(sJWT)
    return sJWT;
  }

  console.log("############################## ")


  try{
      console.log("Preparing assertion...")
      var jwt = createJWT()
      console.log("Assertion: "+jwt);

      var options = {
          method: 'POST',
          headers: {
            'Content-Type':'application/x-www-form-urlencoded'
          },
          url: "https://www.googleapis.com/oauth2/v3/token",
          params: {
            grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",

            assertion: jwt
          }
      };

      console.log("------------------------");
      console.log(options);
      console.log("------------------------");

      Parse.Cloud.httpRequest(options).done(function(rsp){
          var r = (_.isString(rsp.text)) ? JSON.parse(rsp.text) : rsp.text;
          console.log("Reponse from Google:");
          console.log(rsp)
          // console.log({ body: req.body, params: req.params, query: req.query, o: options, r: r });
          // res.send(r);
          response.success(r);

      }).fail(function(err){
          // console.error(err);
          console.error("Failed response from Google:")
          console.error(err.text)
          response.error(err);

      });
    }catch(err){
        console.error(err);
        response.error(err);
    }
});

控制台输出:

I2015-06-29T19:42:17.315Z]############################## 
I2015-06-29T19:42:17.316Z]Preparing assertion...
I2015-06-29T19:42:17.317Z]Preparing to generate RS256 JWT
I2015-06-29T19:42:17.401Z]RS256 JWT generation complete:
I2015-06-29T19:42:17.402Z]eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI2MDQ4Mjk2NTQ1MzktdDE4YjNocmthYjZ1cmlyZWJsbThrYjRrdDQ1YzkyYTJAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvYW5hbHl0aWNzLnJlYWRvbmx5IiwiYXVkIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vb2F1dGgyL3YzL3Rva2VuIiwiZXhwIjoxNDM1NjEwNTM3LCJpYXQiOjE0MzU2MDY5Mzd9.nGbApndzwwtadeL2Jr2zU__JZrBZ6tYGJ17sTDksiSsFRXop_6CFAsV7fkXC6Xd-Nf3KfYzNuqGzLciQTzc9AhGNFTk_aUXU-ndMbYiVh3EpTkBI0olkS5rkgnmm3Q_yfaOswkyvMwE12RvgTTjymVzHGTZ8xC_x22Ep1n07Ap3TQn3WpeFeJlHciiwcxMTG7TsxAvHEgaqLzZ79feFmZanj6pqEH1kfZeJUQK1n3bwKtU92qpPn7b4dFtJs8I7El62HLExU1B2l7qdSyp4CRxmUPViUfWykElDZeqDzPoX38QEMDmmTgCYUXna7wJB6O0qC3aJpxkCAmzPCDkXkZQ
I2015-06-29T19:42:17.403Z]Assertion: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI2MDQ4Mjk2NTQ1MzktdDE4YjNocmthYjZ1cmlyZWJsbThrYjRrdDQ1YzkyYTJAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvYW5hbHl0aWNzLnJlYWRvbmx5IiwiYXVkIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vb2F1dGgyL3YzL3Rva2VuIiwiZXhwIjoxNDM1NjEwNTM3LCJpYXQiOjE0MzU2MDY5Mzd9.nGbApndzwwtadeL2Jr2zU__JZrBZ6tYGJ17sTDksiSsFRXop_6CFAsV7fkXC6Xd-Nf3KfYzNuqGzLciQTzc9AhGNFTk_aUXU-ndMbYiVh3EpTkBI0olkS5rkgnmm3Q_yfaOswkyvMwE12RvgTTjymVzHGTZ8xC_x22Ep1n07Ap3TQn3WpeFeJlHciiwcxMTG7TsxAvHEgaqLzZ79feFmZanj6pqEH1kfZeJUQK1n3bwKtU92qpPn7b4dFtJs8I7El62HLExU1B2l7qdSyp4CRxmUPViUfWykElDZeqDzPoX38QEMDmmTgCYUXna7wJB6O0qC3aJpxkCAmzPCDkXkZQ
I2015-06-29T19:42:17.404Z]------------------------
I2015-06-29T19:42:17.405Z]{"method":"POST","headers":{"Content-Type":"application/x-www-form-urlencoded"},"url":"https://www.googleapis.com/oauth2/v3/token","params":{"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer","assertion":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI2MDQ4Mjk2NTQ1MzktdDE4YjNocmthYjZ1cmlyZWJsbThrYjRrdDQ1YzkyYTJAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvYW5hbHl0aWNzLnJlYWRvbmx5IiwiYXVkIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vb2F1dGgyL3YzL3Rva2VuIiwiZXhwIjoxNDM1NjEwNTM3LCJpYXQiOjE0MzU2MDY5Mzd9.nGbApndzwwtadeL2Jr2zU__JZrBZ6tYGJ17sTDksiSsFRXop_6CFAsV7fkXC6Xd-Nf3KfYzNuqGzLciQTzc9AhGNFTk_aUXU-ndMbYiVh3EpTkBI0olkS5rkgnmm3Q_yfaOswkyvMwE12RvgTTjymVzHGTZ8xC_x22Ep1n07Ap3TQn3WpeFeJlHciiwcxMTG7TsxAvHEgaqLzZ79feFmZanj6pqEH1kfZeJUQK1n3bwKtU92qpPn7b4dFtJs8I7El62HLExU1B2l7qdSyp4CRxmUPViUfWykElDZeqDzPoX38QEMDmmTgCYUXna7wJB6O0qC3aJpxkCAmzPCDkXkZQ"}}
I2015-06-29T19:42:17.406Z]------------------------
I2015-06-29T19:42:17.504Z]Failed response from Google:
I2015-06-29T19:42:17.506Z]{
 "error": "invalid_grant",
 "error_description": "Bad Request"
}

最终解决方案:导入 google 服务帐户凭据 json 文件(重命名为 google-service-account-credentials.js),生成 jwt,将 jwt 应用为 Parse.Request 中的正文而不是参数。

var fs = require('fs');
var moment = require('moment');
var _ = require('underscore');
var KJUR = require("cloud/lib/jsrsasign/npm/lib/jsrsasign.js");
var googleServiceAccountCredentials = JSON.parse(fs.readFileSync('cloud/google-service-account-credentials.js'));


var createJWT = function(args, credentials){

  var header = {
    alg:"RS256",
    typ:"JWT"
  };

  var now = moment().unix();

  var defaults = {
    iss: credentials.client_email,
    scope:"https://www.googleapis.com/auth/analytics.readonly",
    aud:"https://www.googleapis.com/oauth2/v3/token",
    exp: now + (15*60),
    iat: now
  };

  var data = {};

  _.extend(data, defaults, args);

  var sJWT = KJUR.jws.JWS.sign("RS256", JSON.stringify(header), JSON.stringify(data), credentials.private_key );
  return sJWT;
};

Parse.Cloud.define("testBase", function(request, response) {


  try{
      var now = moment().unix();

      var options = {
          method: 'POST',
          headers: {
            'Content-Type':'application/x-www-form-urlencoded'
          },
          url: "https://www.googleapis.com/oauth2/v3/token",
          body: {
            grant_type: encodeURI("urn:ietf:params:oauth:grant-type:jwt-bearer"),
            assertion: createJWT({
              exp: now + (60*60),
              iat: now
            }, googleServiceAccountCredentials)
          }
      };

      Parse.Cloud.httpRequest(options).done(function(rsp){
          var r = (_.isString(rsp.text)) ? JSON.parse(rsp.text) : rsp.text;
          console.log("Reponse from Google:");
          console.log(r.access_token);
          console.log(r.expires_in);
          console.log(r.token_type);
          response.success(r);

      }).fail(function(err){
          console.error("Failed response from Google:")
          console.error(err.text)
          response.error(err);

      });
    }catch(err){
        console.error(err);
        response.error(err);
    }
});

在请求选项中使用 body 而不是 params

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Google OAuth2 服务帐户 HTTP/REST 身份验证 的相关文章

随机推荐

  • Kinesis 流待处理消息计数

    我正在尝试将 AWS Kinesis 流用于我们的数据流之一 我想出于操作目的监视流上的待处理消息 根据积压向下游扩展 但无法找到任何在我的流中提供 大约 待处理消息的 API 这看起来很奇怪 因为消息在 7 天后就会过期 如果生产者和消费
  • Java 程序为音频文件创建 PNG 波形

    如何使用 Java 将 Wav 文件转换为 PNG 波形图像文件 java MyProgram class path to wav file path where to write png file 预期成绩 指定路径中保存的png是传入的
  • 更改 .png 以与 JpegImagesToMovie.java 一起使用

    我正在使用 JpegImagesToMovie java 将图像转换为 mov 文件 我想知道是否可以编辑它来处理 png 文件 因为视频的质量不是很好 更改它会改善它 ImageIO write img png new File C Us
  • 何时使用define_singleton_method v Define_method

    在一个答案中这个问题 https stackoverflow com questions 19368437 how to use define method inside initialize用户 mu 太短 解释说您不希望对象的行为在初始
  • QML Row 与 RowLayout

    我正在尝试为我的应用程序编写一个顶栏 其中应主要包含应用程序徽标 小图像 和应用程序标题 仅文本 此外 我希望这个顶栏能够根据窗口的高度自动调整大小 我是 QML 新手 但我想我应该将这些组件包装在Row or a RowLayout ht
  • Swift 在 UILongPress 上保留 UISwitch 状态

    我在我的项目中添加了 UISwitch 上的 UILongPressGestureRecognizer class ViewController UIViewController UIGestureRecognizerDelegate va
  • 从 sqlite 触发器调用 Java 方法 (android)

    我想要使 用 sqlite 触发器注册回调方法 例如 public void printLog Log i TAG 1 row added 在 sqlite 中插入任何行后调用此方法 是否可以 怎么做 SQLite 提供数据变更通知回调 h
  • 使用 GWT 使用 XMLHttpRequest 发送 XML

    目前我使用 JavaScript 代码在 GWT 中发送 XML 数据 有没有更简单的方法来发送它们 sendRequest addClickHandler new ClickHandler Override public void onC
  • Swift:键盘显示时向上滚动视图

    我有一个滚动视图 我想在显示键盘时向上滚动 当键盘显示时 我因以下错误而崩溃 2014 09 29 14 48 50 738 swrd 1563 472888 swrd EditPhotoViewController KeyboardWas
  • 将参数传递给 JSF 中的视图作用域 bean

    假设我的 JSF 2 应用程序中有两个页面 第一页显示对象表 汽车或其他内容 另一个页面能够显示一个特定对象的详细信息 表页位于请求范围内 因为每次用户请求时都应重新加载对象 详细信息页面在视图范围内 因此 当我单击表格内的某个对象时 该对
  • Android 中使用 DateFormat 的 java.lang.IllegalArgumentException

    这是我的代码片段 这里的日期格式为 10 Sep 2013 09 53 37 TextView tvDate TextView convertView findViewById R id entered date DateFormat da
  • 如何在 python 3 中通过 FTP 发送 StringIO?

    我想通过 FTP 将文本字符串作为文件上传 import ftplib from io import StringIO file StringIO file write aaa file seek 0 with ftplib FTP as
  • 在 jQuery 中绑定多个事件

    我有一个自定义 jQuery 插件 它绑定了change事件到表单元素 在本例中是一些input元素 这change事件用于向其他一些组件指示发生了更改 这按其应该的方式工作 但是 我发现需要绑定一个额外的change事件相同input元素
  • 为什么 C 中不允许将数组大小作为常量变量,而 C++ 中允许?

    我试着写一个c程序如下 const int x 5 int main int arr x 1 2 3 4 5 当我尝试使用 gcc 进行编译时 会发出警告 如下所示 simple c 9 错误 可变大小的对象可能未初始化 但在 C 中也是允
  • 反应本机 fs 库不写入文件

    当我使用react native fs创建文件时 回调成功 但该文件不在我的android data com myapp files test txt库中 logcat 中没有错误 所以我想知道为什么我的简单 App js 代码不起作用 S
  • 使用 Kotlin 和 Mockito 模拟通用接口

    我正在尝试使用 Mockito 模拟 Kotlin 中的通用接口 但到目前为止我还没有找到自然的解决方案 鉴于 interface X
  • NSManagedObjectContext 已锁定

    我的应用程序中有两个线程在运行 在主线程中 我更新实体中键的值或从核心数据获取一些行 在后台线程中 我从服务器下载数据 但有时在更新 处理核心数据时 managedObjectContext executeFetchRequest requ
  • 角度错误 TS2531:对象可能为“空”

    所以我有一个 Component html 其中包含如下输入
  • Android 拆分无法正常工作

    在过去的 4 个月里 我一直在开发 Android 应用程序 并遇到了以下有关拆分功能的问题 String arr SoapPrimitive result SoapPrimitive envelope getResponse arr re
  • Google OAuth2 服务帐户 HTTP/REST 身份验证

    我正在尝试使用下面的文档发出访问令牌请求 https developers google com identity protocols OAuth2ServiceAccount https developers google com ide