google-auth-library-nodejs icon indicating copy to clipboard operation
google-auth-library-nodejs copied to clipboard

`Impersonated` credentials should implement `sign()` capability

Open salrashid123 opened this issue 2 years ago • 0 comments

Impersonated client does not have any way to generate signedurl or to signbytes.

This credential type uses the IAM api to perform acquire various token types like generateAccessToken, generateIdToken (ref: https://github.com/googleapis/google-auth-library-nodejs/issues/1318),

The ability to sign on behalf of the impersonated svc account through the iam api would use the signBlob API.

This feaure request is to surface a sign interface such that it can be used standalone or though a client such as storage and allow generation of signedURLs.

Essentially this flow will allow

  • user -> impersonate svc1 --> generate signedurl as svc1

if it helps, I have a sample working below but i had to make some edits

A) https://github.com/googleapis/google-auth-library-nodejs/blob/main/src/auth/googleauth.ts#L814

async getCredentialsAsync() {
    const client = await this.getClient();

    if (client instanceof impersonated_1.Impersonated) {
        // not really used.  Impersonated uses the service accounts email
        // it got initialized with
        return { client_email: client.targetPrincipal }
    }
  // ...
  // ...
}

B) https://github.com/googleapis/google-auth-library-nodejs/blob/main/src/auth/googleauth.ts#L946

async sign(data) {
    const client = await this.getClient();
    
    if (client instanceof impersonated_1.Impersonated) {
        return client.sign(data);
    }
  // ...
  // ...
}

C) https://github.com/googleapis/google-auth-library-nodejs/blob/main/src/auth/impersonated.ts

/**
 * Signs some bytes.
 * @param blobToSign Sign bytes.
 */
     async sign(blobToSign) {

        try {
            await this.sourceClient.getAccessToken();
            const name = `projects/-/serviceAccounts/${this.targetPrincipal}`;
            const u = `${this.endpoint}/v1/${name}:signBlob`;
            const body = {
                delegates: this.delegates,
                payload: Buffer.from(blobToSign).toString('base64')
            };
            const res = await this.sourceClient.request({
                url: u,
                data: body,
                method: 'POST',
            });
            const tokenResponse = res.data;
            return tokenResponse.signedBlob
        }
        catch (error) {

        }
    }    
Usage
const { GoogleAuth, Impersonated } = require('google-auth-library');
const { Storage } = require('@google-cloud/storage');

async function main() {
  const scopes = 'https://www.googleapis.com/auth/cloud-platform'
  // get source credentials
  const auth = new GoogleAuth({
    scopes: scopes
  });
  const client = await auth.getClient();

  // First impersonate
  let targetPrincipal = '[email protected]'
  let targetClient = new Impersonated({
    sourceClient: client,
    targetPrincipal: targetPrincipal,
    lifetime: 30,
    delegates: [],
    targetScopes: [scopes]
  });

  // test sign
  //console.log(await targetClient.sign("foo"));

  let projectId = 'fabled-ray-104117'
  let bucketName = 'fabled-ray-104117-test'

  // use the impersonated creds to bootstrap a storage client
  const storageOptions = {
    projectId,
    authClient: targetClient,
  };
  const storage = new Storage(storageOptions);

  // ####### GCS SignedURL
  const signOptions = {
    version: 'v4',
    action: 'read',
    expires: Date.now() + 15 * 60 * 1000, // 15 minutes
  };

  const  su = await storage
    .bucket(bucketName)
    .file('foo.txt')
    .getSignedUrl(signOptions);
  console.log(su);
}
main().catch(console.error);

ref https://github.com/googleapis/google-auth-library-nodejs/issues/1210 Google Cloud Storage SignedURL with Cloud Run, Cloud Functions and GCE VMs

@danielbankhead, @FrodoTheTrue, @m0ar

salrashid123 avatar Aug 22 '22 11:08 salrashid123