【问题标题】:How to obtain Google service account access token javascript如何获取 Google 服务帐户访问令牌 javascript
【发布时间】:2015-04-29 09:26:22
【问题描述】:

我正在尝试为我的网站设置一个分析仪表板,以便我的领导层查看网站使用情况。我不希望他们必须拥有谷歌帐户或单独添加他们才能查看结果。

我已设置服务帐号和 OAuth2 访问权限。我找到的所有教程都显示这样的代码:

gapi.analytics.auth.authorize({
  clientid: 'Service account client ID',
  serverAuth: {
      access_token: 'XXXXXXXXXXXXXXXXX'
}

所有文档都在谈论“......一旦你收到你的访问令牌......”但他们都没有真正说明如何获得它!我看到证书指纹、公钥指纹。我还看到了如何生成 JSON 和 P12 密钥。我看不到如何生成访问令牌。

有人能解释一下怎么做吗?

我找到了this。它解释说我需要密钥文件,这是一个坏主意,但没有说明如何实际操作。

我还找到了this。但我对 Node.js 一无所知,我希望这只是一种可能的途径?

【问题讨论】:

  • 我找到了这个javascript库kjur.github.io/jsjws/index.html#demo有人有这方面的经验吗?
  • 从哪里获得我的私钥?我已经下载了 .p12 文件,但我无法读取它。我使用在线服务将 .p12 转换为 PEM 文件,我可以在记事本中打开该文件,但与我见过的其他人相比,那里显示的私钥太短了。

标签: javascript google-analytics-api


【解决方案1】:

终于搞定了!使用 kjur 的 jsjws 纯 JavaScript 实现 JWT。我使用this demo 作为生成JWT 来请求令牌的基础。以下是步骤

在 Google Developers 控制台中,我创建了一个服务帐户。以下是相关说明

在 Google API 控制台中,我将服务帐户添加到凭据中。然后我生成了一个新的 JSON 密钥。这给了我纯文本格式的私钥

然后我按照 google 的这些说明使用 HTTP/REST 进行授权 API 调用。

这是必需的标头信息。

var pHeader = {"alg":"RS256","typ":"JWT"}
var sHeader = JSON.stringify(pHeader);

声明集是这样的。 (这是使用上述 KJUR JWT 库提供的语法。)

var pClaim = {};
pClaim.aud = "https://www.googleapis.com/oauth2/v3/token";
pClaim.scope = "https://www.googleapis.com/auth/analytics.readonly";
pClaim.iss = "<serviceAccountEmail@developer.gserviceaccount.com";
pClaim.exp = KJUR.jws.IntDate.get("now + 1hour");
pClaim.iat = KJUR.jws.IntDate.get("now");

var sClaim = JSON.stringify(pClaim);

有争议的一点是将我的私钥放入客户端代码中。对于这种用法,它并没有那么糟糕(我不认为。)首先,该站点位于我们公司的防火墙后面,那么谁来“破解”它?其次,即使有人得到它,服务帐户的唯一授权是查看我们的分析数据——我的仪表板的目的是任何访问该页面的人都可以查看我们的分析数据。这里就不贴私钥了,基本就这样吧。

var key = "-----BEGIN PRIVATE KEY-----\nMIIC....\n-----END PRIVATE KEY-----\n";`enter code here`

然后用

生成一个签名的JWT
 var sJWS = KJUR.jws.JWS.sign(null, sHeader, sClaim, key);

之后我使用 XMLHttpRequest 调用 google API。我尝试将 FormData 与请求一起使用,但这没有用。所以老(呃)学校

var XHR = new XMLHttpRequest();
var urlEncodedData = "";
var urlEncodedDataPairs = [];

urlEncodedDataPairs.push(encodeURIComponent("grant_type") + '=' + encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"));
urlEncodedDataPairs.push(encodeURIComponent("assertion") + '=' + encodeURIComponent(sJWS));
urlEncodedData = urlEncodedDataPairs.join('&').replace(/%20/g, '+');

// We define what will happen if the data are successfully sent
XHR.addEventListener('load', function(event) {
    var response = JSON.parse(XHR.responseText);
    token = response["access_token"]
});

// We define what will happen in case of error
XHR.addEventListener('error', function(event) {
    console.log('Oops! Something went wrong.');
});

XHR.open('POST', 'https://www.googleapis.com/oauth2/v3/token');
XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
XHR.send(urlEncodedData)

之后我有了访问令牌,我可以关注these tutorials 使用嵌入 API,但像这样授权:

gapi.analytics.auth.authorize({
    serverAuth: {
        access_token: token
    }
});

不要忘记您必须授予服务帐户查看内容的权限,就像任何其他用户一样。当然,如果服务帐户被授权执行只读以外的任何操作,那将是一个非常糟糕的主意。

我可能还会遇到关于时间和令牌过期的问题,但到目前为止一切都很好。

【讨论】:

  • 因为我没有 10 个代表,所以我不能发布超过 2 个链接。因此,如果有人需要知道我在哪里找到了各种教程,请询问,我可以分享。
  • 已经有一段时间了。但我刚刚又试了一次。在开发者控制台中创建一个新的服务帐户。那时它使您能够下载 json 密钥。用文本编辑打开文件,关键是“私钥”属性的值。它以 -----BEGIN PRIVATE KEY----- 开头并以 -----END PRIVATE KEY-----\n 结尾请记住,私钥应保密,不得向最终用户公开或包含在内在将传递给浏览器的 javascript 文件中。所有加密的东西都应该发生在服务器 php、aspx、nodejs 等......
  • 你不知道我找了多久。我花了很多时间在网上搜索一个完整的、循序渐进的示例(Google 的文档太糟糕了!)。非常感谢你。
  • 感谢@Rothrock 的回答。我现在遇到了错误"errorDetail": "JWS Head is not safe JSON string: {'alg':'RS256','typ':'JWT'} (jsjws/jws3.2.js#407)", "statusCode": "500"。知道为什么吗?我正在尝试在 [this site](scriptr.io) 上创建一个脚本,所以我的项目是无头的(没有窗口元素)。
  • 截至 01/2018,我建议使用 github.com/kjur/jsrsasign 而不是 jsjws - 它在内部包含 jsjws 并修复了 @CaffeinatedCoder 的问题。它只是一个插入式替换,函数调用完全相同。顺便说一句,感谢您的回答,我在测试新的 FCM HTTP v1 API 时使用了它。
【解决方案2】:

您可以使用 Node.js 的官方(和 alpha)Google API 来生成令牌。如果您有服务帐户,这会很有帮助。

在服务器上:

npm install -S googleapis

ES6:

import google from 'googleapis'
import googleServiceAccountKey from '/path/to/private/google-service-account-private-key.json' // see docs on how to generate a service account

const googleJWTClient = new google.Auth.JWT(
  googleServiceAccountKey.client_email,
  null,
  googleServiceAccountKey.private_key,
  ['https://www.googleapis.com/auth/analytics.readonly'], // You may need to specify scopes other than analytics
  null,
)

googleJWTClient.authorize((error, access_token) => {
   if (error) {
      return console.error("Couldn't get access token", e)
   }
   // ... access_token ready to use to fetch data and return to client
   // even serve access_token back to client for use in `gapi.analytics.auth.authorize`
})

【讨论】:

  • 有PHP版本吗?
【解决方案3】:

现在,服务帐户身份验证有一个 getAccessToken 方法可以为我们做这件事。

const {google} = require('googleapis');

const main = async function() {
    
    const auth = new google.auth.GoogleAuth({
        keyFile: __dirname  + '/service-account-key.json',
        scopes: [ 'https://www.googleapis.com/auth/cloud-platform']
    });

    const accessToken = await auth.getAccessToken()
    
    console.log(JSON.stringify(auth, null, 4))
    console.log(JSON.stringify(accessToken, null, 4));
}

main().then().catch(err => console.log(err));

【讨论】:

  • 我遵循你的逻辑,我收到以下错误:FetchError: request to googleapis.com/oauth2/v4/token failed, reason: getaddrinfo ENOTFOUND 808
  • 我就是这样用的。但是得到了“abcxyz......”这种类型的访问令牌,最后一个字符都是“点”,不能使用。你知道为什么吗?
【解决方案4】:

我在寻找类似的东西时遇到了这个问题,并认为我会分享一个我最终得到的 node.js 解决方案。本质上,我将一个 google 服务帐户保存到一个 sa.json 文件中,然后用它来签署我发送给 gcp 的 jwt。

const jwt = require("jsonwebtoken");
const sa = require("./sa.json");
const fetch = require("isomorphic-fetch");

const authUrl = "https://www.googleapis.com/oauth2/v4/token";
const scope = "https://www.googleapis.com/auth/cloud-platform";

const getSignedJwt = () => {
  const token = {
    iss: sa.client_email,
    iat: parseInt(Date.now() / 1000),
    exp: parseInt(Date.now() / 1000) + 60 * 60, // 60 minutes
    aud: authUrl,
    scope,
  };

  return jwt.sign(token, sa.private_key, { algorithm: "RS256" });
};

const getGoogleAccessToken = async () => {
  const signedJwt = getSignedJwt();
  const body = new URLSearchParams();

  body.append("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
  body.append("assertion", signedJwt);

  const response = await fetch(authUrl, {
    method: "post",
    headers: {
      Authorization: `Bearer ${signedJwt}`,
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body,
  });
  return response.json();
};

(async () => {
  const tokenResp = await getGoogleAccessToken();
  console.log(tokenResp);
})();

【讨论】:

    【解决方案5】:

    你有(下面)没有单引号

    gapi.analytics.auth.authorize({
        serverAuth: {
            access_token: token
        }
    });
    

    但要使其正常工作,根据他们的文档,您需要在 serverAuthaccess_token 周围加上单引号。

    gapi.analytics.auth.authorize({
        'serverAuth': {
            'access_token': token
        }
    });
    

    【讨论】:

    猜你喜欢
    • 2017-10-07
    • 2016-04-29
    • 1970-01-01
    • 1970-01-01
    • 2015-02-11
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 2019-01-05
    相关资源
    最近更新 更多