File

src/auth/impersonated.ts

Extends

RefreshOptions

Index

Properties

Properties

delegates
delegates: string[]
Type : string[]
Optional

The chained list of delegates required to grant the final access_token.

endpoint
endpoint: string
Type : string
Optional

API endpoint to fetch token from.

lifetime
lifetime: number | "3600"
Type : number | "3600"
Optional

Number of seconds the delegated credential should be valid.

sourceClient
sourceClient: AuthClient
Type : AuthClient
Optional

Client used to perform exchange for impersonated client.

targetPrincipal
targetPrincipal: string
Type : string
Optional

The service account to impersonate.

targetScopes
targetScopes: string[]
Type : string[]
Optional

Scopes to request during the authorization grant.

import {GetTokenResponse, OAuth2Client, RefreshOptions} from './oauth2client';
import {AuthClient} from './authclient';

export interface ImpersonatedOptions extends RefreshOptions {
  /**
   * Client used to perform exchange for impersonated client.
   */
  sourceClient?: AuthClient;
  /**
   * The service account to impersonate.
   */
  targetPrincipal?: string;
  /**
   * Scopes to request during the authorization grant.
   */
  targetScopes?: string[];
  /**
   * The chained list of delegates required to grant the final access_token.
   */
  delegates?: string[];
  /**
   * Number of seconds the delegated credential should be valid.
   */
  lifetime?: number | 3600;
  /**
   * API endpoint to fetch token from.
   */
  endpoint?: string;
}

export interface TokenResponse {
  accessToken: string;
  expireTime: string;
}

export class Impersonated extends OAuth2Client {
  private sourceClient: AuthClient;
  private targetPrincipal: string;
  private targetScopes: string[];
  private delegates: string[];
  private lifetime: number;
  private endpoint: string;

  /**
   * Impersonated service account credentials.
   *
   * Create a new access token by impersonating another service account.
   *
   * Impersonated Credentials allowing credentials issued to a user or
   * service account to impersonate another. The source project using
   * Impersonated Credentials must enable the "IAMCredentials" API.
   * Also, the target service account must grant the orginating principal
   * the "Service Account Token Creator" IAM role.
   *
   * @param {object} options - The configuration object.
   * @param {object} [options.sourceClient] the source credential used as to
   * acquire the impersonated credentials.
   * @param {string} [options.targetPrincipal] the service account to
   * impersonate.
   * @param {string[]} [options.delegates] the chained list of delegates
   * required to grant the final access_token. If set, the sequence of
   * identities must have "Service Account Token Creator" capability granted to
   * the preceding identity. For example, if set to [serviceAccountB,
   * serviceAccountC], the sourceCredential must have the Token Creator role on
   * serviceAccountB. serviceAccountB must have the Token Creator on
   * serviceAccountC. Finally, C must have Token Creator on target_principal.
   * If left unset, sourceCredential must have that role on targetPrincipal.
   * @param {string[]} [options.targetScopes] scopes to request during the
   * authorization grant.
   * @param {number} [options.lifetime] number of seconds the delegated
   * credential should be valid for up to 3600 seconds by default, or 43,200
   * seconds by extending the token's lifetime, see:
   * https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth
   * @param {string} [options.endpoint] api endpoint override.
   */
  constructor(options: ImpersonatedOptions = {}) {
    super(options);
    this.credentials = {
      expiry_date: 1,
      refresh_token: 'impersonated-placeholder',
    };
    this.sourceClient = options.sourceClient ?? new OAuth2Client();
    this.targetPrincipal = options.targetPrincipal ?? '';
    this.delegates = options.delegates ?? [];
    this.targetScopes = options.targetScopes ?? [];
    this.lifetime = options.lifetime ?? 3600;
    this.endpoint = options.endpoint ?? 'https://iamcredentials.googleapis.com';
  }

  /**
   * Refreshes the access token.
   * @param refreshToken Unused parameter
   */
  protected async refreshToken(
    refreshToken?: string | null
  ): Promise<GetTokenResponse> {
    try {
      await this.sourceClient.getAccessToken();
      const name = 'projects/-/serviceAccounts/' + this.targetPrincipal;
      const u = `${this.endpoint}/v1/${name}:generateAccessToken`;
      const body = {
        delegates: this.delegates,
        scope: this.targetScopes,
        lifetime: this.lifetime + 's',
      };
      const res = await this.sourceClient.request<TokenResponse>({
        url: u,
        data: body,
        method: 'POST',
      });
      const tokenResponse = res.data;
      this.credentials.access_token = tokenResponse.accessToken;
      this.credentials.expiry_date = Date.parse(tokenResponse.expireTime);
      return {
        tokens: this.credentials,
        res,
      };
    } catch (error) {
      const status = error?.response?.data?.error?.status;
      const message = error?.response?.data?.error?.message;
      if (status && message) {
        error.message = `${status}: unable to impersonate: ${message}`;
        throw error;
      } else {
        error.message = `unable to impersonate: ${error}`;
        throw error;
      }
    }
  }
}

result-matching ""

    No results matching ""