通过 nodejs 使用不记名令牌进行谷歌云身份验证

Jan*_*pan 3 google-api-nodejs-client google-cloud-platform gcloud google-cloud-run

我的客户有一个在 Google Cloud Run 上运行的 GraphQL API。

我收到了一个用于身份验证的服务帐户以及对 gcloud 命令行工具的访问权限。

像这样使用 gcloud 命令行时:

gcloud auth print-identity-token
Run Code Online (Sandbox Code Playgroud)

我可以生成一个令牌,该令牌可用于向 api 发出发布请求。这有效,我可以从邮递员、失眠症和我的 nodejs 应用程序成功地向 api 发出请求。

但是,当我将 JWT 身份验证与“googleapis”或“google-auth”npm 库一起使用时,如下所示:

var { google } = require('googleapis')

let privatekey = require('./auth/google/service-account.json')

let jwtClient = new google.auth.JWT(
  privatekey.client_email,
  null,
  privatekey.private_key,
  ['https://www.googleapis.com/auth/cloud-platform']
)

jwtClient.authorize(function(err, _token) {
  if (err) {
    console.log(err)
    return err
  } else {
    console.log('token obj:', _token)
  }
})
Run Code Online (Sandbox Code Playgroud)

这将输出一个“承载”令牌:

token obj: {
  access_token: 'ya29.c.Ko8BvQcMD5zU-0raojM_u2FZooWMyhB9Ni0Yv2_dsGdjuIDeL1tftPg0O17uFrdtkCuJrupBBBK2IGfUW0HGtgkYk-DZiS1aKyeY9wpXTwvbinGe9sud0k1POA2vEKiGONRqFBSh9-xms3JhZVdCmpBi5EO5aGjkkJeFI_EBry0E12m2DTm0T_7izJTuGQ9hmyw',
  token_type: 'Bearer',
  expiry_date: 1581954138000,
  id_token: undefined,
  refresh_token: 'jwt-placeholder'
}
Run Code Online (Sandbox Code Playgroud)

然而,这个不记名令牌不能像上面那样工作,并且在发出与 gcloud 命令“gcloud auth print-identity-token”相同的请求时,总是会给出“未经授权的错误 401”。

请帮忙,我不确定为什么第一个不记名令牌有效,但使用 JWT 生成的令牌无效。

编辑

我还尝试获取身份令牌而不是像这样的访问令牌:

let privatekey = require('./auth/google/service-account.json')

let jwtClient = new google.auth.JWT(
  privatekey.client_email,
  null,
  privatekey.private_key,
  []
)

jwtClient
  .fetchIdToken('https://my.audience.url')
  .then((res) => console.log('res:', res))
  .catch((err) => console.log('err', err))
Run Code Online (Sandbox Code Playgroud)

这会打印一个身份令牌,但是,使用它也只会给出“401 未经授权”的消息。

编辑以显示我如何调用端点

只是附带说明,下面的任何这些方法都可以与命令行身份令牌一起使用,但是当通过 JWT 生成时,它会返回 401

方法一:

 const client = new GraphQLClient(baseUrl, {
        headers: {
          Authorization: 'Bearer ' + _token.id_token
        }
      })
      const query = `{
        ... my graphql query goes here ...
    }`
      client
        .request(query)
        .then((data) => {
          console.log('result from query:', data)
          res.send({ data })
          return 0
        })
        .catch((err) => {
          res.send({ message: 'error ' + err })
          return 0
        })
    }
Run Code Online (Sandbox Code Playgroud)

方法 2(使用我用 google-auth 创建的“授权”客户端):

  const res = await client.request({
    url: url,
    method: 'post',
    data: `{
        My graphQL query goes here ...
    }`
  })
  console.log(res.data)
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*ley 6

下面是 node.js 中的一个示例,该示例正确创建了具有正确受众的身份令牌,用于调用 Cloud Run 或 Cloud Functions 服务。

修改此示例以适合 GraphQLClient。不要忘记在每次调用中包含 Authorization 标头。

    // This program creates an OIDC Identity Token from a service account
    // and calls an HTTP endpoint with the Identity Token as the authorization
    
    var { google } = require('googleapis')
    const request = require('request')
    
    // The service account JSON key file to use to create the Identity Token
    let privatekey = require('/config/service-account.json')
    
    // The HTTP endpoint to call with an Identity Token for authorization
    // Note: This url is using a custom domain. Do not use the same domain for the audience
    let url = 'https://example.jhanley.dev'
    
    // The audience that this ID token is intended for (example Google Cloud Run service URL)
    // Do not use a custom domain name, use the Assigned by Cloud Run url
    let audience = 'https://example-ylabperdfq-uc.a.run.app'
    
    let jwtClient = new google.auth.JWT(
        privatekey.client_email,
        null,
        privatekey.private_key,
        audience
    )
    
    jwtClient.authorize(function(err, _token) {
        if (err) {
            console.log(err)
            return err
        } else {
            // console.log('token obj:', _token)
    
            request(
                {
                    url: url,
                    headers: {
                        "Authorization": "Bearer " + _token.id_token
                    }
                },
                function(err, response, body) {
                    if (err) {
                        console.log(err)
                        return err
                    } else {
                        // console.log('Response:', response)
                        console.log(body)
                    }
                }
            );
        }
    })
Run Code Online (Sandbox Code Playgroud)