如何获取 Google 服务帐户访问令牌 javascript

How to obtain Google service account access token javascript

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

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

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

所有文档都在谈论“......一旦你收到你的访问令牌......”但是 none 他们实际上说了如何获得它!我看到证书指纹,Public 密钥指纹。我还看到了如何生成 JSON 和 P12 密钥。我看不到如何生成访问令牌。

谁能解释一下如何做到这一点?

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

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

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

在 Google 开发人员控制台中,我创建了一个服务帐户。这是相关说明

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

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

这是必需的 header 信息。

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);

有争议的一点是将我的私钥放入客户端代码。对于这种用法,它并没有那么糟糕(我不认为。)首先,该站点在我们公司的防火墙后面,所以谁会 "hack" 它?其次,即使有人确实得到它,服务帐户的唯一授权是查看我们的分析数据——我的仪表板的目的是任何访问该页面的人都可以查看我们的分析数据。这里不打算post私钥,但基本上都是这样。

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
    }
});

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

可能还有关于时间和令牌过期的问题,我将 运行 解决,但到目前为止一切顺利。

你有(下)没有单引号

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

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

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

您可以使用 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`
})

现在,服务帐户身份验证有一个 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));

我在寻找类似的东西时遇到了这个问题,我想我会分享一个我最终得到的 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);
})();