1

我正在使用自定义服务帐户(使用--service-account部署命令中的参数)。该服务帐户启用了域范围的委派,并安装在 G Apps 管理面板中。

我试过这段代码:

app.get('/test', async (req, res) => {
    const auth = new google.auth.GoogleAuth()
    const gmailClient = google.gmail({ version: 'v1' })
    const { data } = await gmailClient.users.labels.list({ auth, userId: 'user@domain.com' })
    return res.json(data).end()
})

如果我在我的机器上运行它(将GOOGLE_APPLICATION_CREDENTIALSenv var 设置为分配给 Cloud Run 服务的同一服务帐户的路径),它就可以工作,但是当它在 Cloud Run 中运行时,我会收到以下响应:

{
  "code" : 400,
  "errors" : [ {
    "domain" : "global",
    "message" : "Bad Request",
    "reason" : "failedPrecondition"
  } ],
  "message" : "Bad Request"
}

我看到了针对同一问题的解决方案,但它适用于 Python,我不知道如何使用 Node 库复制该行为。

4

2 回答 2

2

经过几天的研究,我终于得到了一个可行的解决方案(移植 Python 实现):

async function getGoogleCredentials(subject: string, scopes: string[]): Promise<JWT | OAuth2Client> {
    const auth = new google.auth.GoogleAuth({
        scopes: ['https://www.googleapis.com/auth/cloud-platform'],
    })
    const authClient = await auth.getClient()

    if (authClient instanceof JWT) {
        return (await new google.auth.GoogleAuth({ scopes, clientOptions: { subject } }).getClient()) as JWT
    } else if (authClient instanceof Compute) {
        const serviceAccountEmail = (await auth.getCredentials()).client_email
        const unpaddedB64encode = (input: string) =>
            Buffer.from(input)
                .toString('base64')
                .replace(/=*$/, '')
        const now = Math.floor(new Date().getTime() / 1000)
        const expiry = now + 3600
        const payload = JSON.stringify({
            aud: 'https://accounts.google.com/o/oauth2/token',
            exp: expiry,
            iat: now,
            iss: serviceAccountEmail,
            scope: scopes.join(' '),
            sub: subject,
        })

        const header = JSON.stringify({
            alg: 'RS256',
            typ: 'JWT',
        })

        const iamPayload = `${unpaddedB64encode(header)}.${unpaddedB64encode(payload)}`

        const iam = google.iam('v1')
        const { data } = await iam.projects.serviceAccounts.signBlob({
            auth: authClient,
            name: `projects/-/serviceAccounts/${serviceAccountEmail}`,
            requestBody: {
                bytesToSign: unpaddedB64encode(iamPayload),
            },
        })
        const assertion = `${iamPayload}.${data.signature!.replace(/=*$/, '')}`

        const headers = { 'content-type': 'application/x-www-form-urlencoded' }
        const body = querystring.encode({ assertion, grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer' })
        const response = await fetch('https://accounts.google.com/o/oauth2/token', { method: 'POST', headers, body }).then(r => r.json())

        const newCredentials = new OAuth2Client()
        newCredentials.setCredentials({ access_token: response.access_token })
        return newCredentials
    } else {
        throw new Error('Unexpected authentication type')
    }
}
于 2020-03-03T11:24:42.640 回答
0

您可以在这里做的是在您的 yaml 文件中定义 ENV 变量,如本文档中所述,以将 GOOGLE_APPLICATION_CREDENTIALS 设置为 JSON 密钥的路径。

然后使用此处提到的代码。

const authCloudExplicit = async ({projectId, keyFilename}) => {
  // [START auth_cloud_explicit]
  // Imports the Google Cloud client library.
  const {Storage} = require('@google-cloud/storage');

  // Instantiates a client. Explicitly use service account credentials by
  // specifying the private key file. All clients in google-cloud-node have this
  // helper, see https://github.com/GoogleCloudPlatform/google-cloud-node/blob/master/docs/authentication.md
  // const projectId = 'project-id'
  // const keyFilename = '/path/to/keyfile.json'
  const storage = new Storage({projectId, keyFilename});

  // Makes an authenticated API request.
  try {
    const [buckets] = await storage.getBuckets();

    console.log('Buckets:');
    buckets.forEach(bucket => {
      console.log(bucket.name);
    });
  } catch (err) {
    console.error('ERROR:', err);
  }
  // [END auth_cloud_explicit]
};

或采用与此处提到的方法类似的方法。

'use strict';

const {auth, Compute} = require('google-auth-library');


async function main() {
  const client = new Compute({
    serviceAccountEmail: 'some-service-account@example.com',
  });
  const projectId = await auth.getProjectId();
  const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
  const res = await client.request({url});
  console.log(res.data);
}

main().catch(console.error);
于 2020-03-02T14:13:06.210 回答