File

src/auth/jwtaccess.ts

Index

Properties
Methods

Constructor

constructor(email?: string | null, key?: string | null, keyId?: string | null)

JWTAccess service account credentials.

Create a new access token by using the credential to create a new JWT token that's recognized as the access token.

Parameters :
Name Type Optional Description
email string | null Yes

the service account email address.

key string | null Yes

the private key that will be used to sign the token.

keyId string | null Yes

the ID of the private key used to sign the token.

Properties

Optional email
Type : string | null
Optional key
Type : string | null
Optional keyId
Type : string | null
Optional projectId
Type : string

Methods

createScopedRequired
createScopedRequired()

Indicates whether the credential requires scopes to be created by calling createdScoped before use.

Returns : boolean

always false

fromJSON
fromJSON(json: JWTInput)

Create a JWTAccess credentials instance using the given input options.

Parameters :
Name Type Optional Description
json JWTInput No

The input object.

Returns : void
fromStream
fromStream(inputStream: stream.Readable)

Create a JWTAccess credentials instance using the given input stream.

Parameters :
Name Type Optional Description
inputStream stream.Readable No

The input stream.

Returns : Promise<void>
fromStream
fromStream(inputStream: stream.Readable, callback: (err: Error) => void)
Parameters :
Name Type Optional
inputStream stream.Readable No
callback function No
Returns : void
fromStream
fromStream(inputStream: stream.Readable, callback?: (err?: Error) => void)
Parameters :
Name Type Optional
inputStream stream.Readable No
callback function Yes
Returns : void | Promise
getRequestHeaders
getRequestHeaders(url: string, additionalClaims?: Claims)

Get a non-expired access token, after refreshing if necessary.

Parameters :
Name Type Optional Description
url string No

The URI being authorized.

additionalClaims Claims Yes

An object with a set of additional claims to include in the payload.

Returns : Headers

An object that includes the authorization header.

getRequestMetadata
getRequestMetadata(url: string, additionalClaims?: Claims)

Get a non-expired access token, after refreshing if necessary.

Parameters :
Name Type Optional Description
url string No
additionalClaims Claims Yes

An object with a set of additional claims to include in the payload.

An object that includes the authorization header.

import * as jws from 'jws';
import * as LRU from 'lru-cache';
import * as stream from 'stream';

import * as messages from '../messages';
import {JWTInput} from './credentials';
import {Headers, RequestMetadataResponse} from './oauth2client';

const DEFAULT_HEADER: jws.Header = {
  alg: 'RS256',
  typ: 'JWT',
};

export interface Claims {
  [index: string]: string;
}

export class JWTAccess {
  email?: string | null;
  key?: string | null;
  keyId?: string | null;
  projectId?: string;

  private cache = new LRU<string, Headers>({max: 500, maxAge: 60 * 60 * 1000});

  /**
   * JWTAccess service account credentials.
   *
   * Create a new access token by using the credential to create a new JWT token
   * that's recognized as the access token.
   *
   * @param email the service account email address.
   * @param key the private key that will be used to sign the token.
   * @param keyId the ID of the private key used to sign the token.
   */
  constructor(
    email?: string | null,
    key?: string | null,
    keyId?: string | null
  ) {
    this.email = email;
    this.key = key;
    this.keyId = keyId;
  }

  /**
   * Indicates whether the credential requires scopes to be created by calling
   * createdScoped before use.
   * @deprecated
   * @return always false
   */
  createScopedRequired(): boolean {
    // JWT Header authentication does not use scopes.
    messages.warn(messages.JWT_ACCESS_CREATE_SCOPED_DEPRECATED);
    return false;
  }

  /**
   * Get a non-expired access token, after refreshing if necessary.
   *
   * @param authURI The URI being authorized.
   * @param additionalClaims An object with a set of additional claims to
   * include in the payload.
   * @deprecated Please use `getRequestHeaders` instead.
   * @returns An object that includes the authorization header.
   */
  getRequestMetadata(
    url: string,
    additionalClaims?: Claims
  ): RequestMetadataResponse {
    messages.warn(messages.JWT_ACCESS_GET_REQUEST_METADATA_DEPRECATED);
    return {headers: this.getRequestHeaders(url, additionalClaims)};
  }

  /**
   * Get a non-expired access token, after refreshing if necessary.
   *
   * @param url The URI being authorized.
   * @param additionalClaims An object with a set of additional claims to
   * include in the payload.
   * @returns An object that includes the authorization header.
   */
  getRequestHeaders(url: string, additionalClaims?: Claims): Headers {
    const cachedToken = this.cache.get(url);
    if (cachedToken) {
      return cachedToken;
    }
    const iat = Math.floor(new Date().getTime() / 1000);
    const exp = iat + 3600; // 3600 seconds = 1 hour

    // The payload used for signed JWT headers has:
    // iss == sub == <client email>
    // aud == <the authorization uri>
    const defaultClaims = {
      iss: this.email,
      sub: this.email,
      aud: url,
      exp,
      iat,
    };

    // if additionalClaims are provided, ensure they do not collide with
    // other required claims.
    if (additionalClaims) {
      for (const claim in defaultClaims) {
        if (additionalClaims[claim]) {
          throw new Error(
            `The '${claim}' property is not allowed when passing additionalClaims. This claim is included in the JWT by default.`
          );
        }
      }
    }

    const header = this.keyId
      ? {...DEFAULT_HEADER, kid: this.keyId}
      : DEFAULT_HEADER;
    const payload = Object.assign(defaultClaims, additionalClaims);

    // Sign the jwt and add it to the cache
    const signedJWT = jws.sign({header, payload, secret: this.key});
    const headers = {Authorization: `Bearer ${signedJWT}`};
    this.cache.set(url, headers);
    return headers;
  }

  /**
   * Create a JWTAccess credentials instance using the given input options.
   * @param json The input object.
   */
  fromJSON(json: JWTInput): void {
    if (!json) {
      throw new Error(
        'Must pass in a JSON object containing the service account auth settings.'
      );
    }
    if (!json.client_email) {
      throw new Error(
        'The incoming JSON object does not contain a client_email field'
      );
    }
    if (!json.private_key) {
      throw new Error(
        'The incoming JSON object does not contain a private_key field'
      );
    }
    // Extract the relevant information from the json key file.
    this.email = json.client_email;
    this.key = json.private_key;
    this.keyId = json.private_key_id;
    this.projectId = json.project_id;
  }

  /**
   * Create a JWTAccess credentials instance using the given input stream.
   * @param inputStream The input stream.
   * @param callback Optional callback.
   */
  fromStream(inputStream: stream.Readable): Promise<void>;
  fromStream(
    inputStream: stream.Readable,
    callback: (err?: Error) => void
  ): void;
  fromStream(
    inputStream: stream.Readable,
    callback?: (err?: Error) => void
  ): void | Promise<void> {
    if (callback) {
      this.fromStreamAsync(inputStream).then(r => callback(), callback);
    } else {
      return this.fromStreamAsync(inputStream);
    }
  }

  private fromStreamAsync(inputStream: stream.Readable): Promise<void> {
    return new Promise((resolve, reject) => {
      if (!inputStream) {
        reject(
          new Error(
            'Must pass in a stream containing the service account auth settings.'
          )
        );
      }
      let s = '';
      inputStream
        .setEncoding('utf8')
        .on('data', chunk => (s += chunk))
        .on('error', reject)
        .on('end', () => {
          try {
            const data = JSON.parse(s);
            this.fromJSON(data);
            resolve();
          } catch (err) {
            reject(err);
          }
        });
    });
  }
}

result-matching ""

    No results matching ""