@@ -24,13 +24,17 @@ import {Crypto, createCrypto} from '../crypto/crypto';
24
24
import { DefaultTransporter , Transporter } from '../transporters' ;
25
25
26
26
import { Compute , ComputeOptions } from './computeclient' ;
27
- import { CredentialBody , JWTInput } from './credentials' ;
27
+ import { CredentialBody , ImpersonatedJWTInput , JWTInput } from './credentials' ;
28
28
import { IdTokenClient } from './idtokenclient' ;
29
29
import { GCPEnv , getEnv } from './envDetect' ;
30
30
import { JWT , JWTOptions } from './jwtclient' ;
31
31
import { Headers , OAuth2ClientOptions , RefreshOptions } from './oauth2client' ;
32
32
import { UserRefreshClient , UserRefreshClientOptions } from './refreshclient' ;
33
- import { Impersonated , ImpersonatedOptions } from './impersonated' ;
33
+ import {
34
+ Impersonated ,
35
+ ImpersonatedOptions ,
36
+ IMPERSONATED_ACCOUNT_TYPE ,
37
+ } from './impersonated' ;
34
38
import {
35
39
ExternalAccountClient ,
36
40
ExternalAccountClientOptions ,
@@ -459,13 +463,72 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
459
463
return this . fromStream ( readStream , options ) ;
460
464
}
461
465
466
+ /**
467
+ * Create a credentials instance using a given impersonated input options.
468
+ * @param json The impersonated input object.
469
+ * @returns JWT or UserRefresh Client with data
470
+ */
471
+ fromImpersonatedJSON ( json : ImpersonatedJWTInput ) : Impersonated {
472
+ if ( ! json ) {
473
+ throw new Error (
474
+ 'Must pass in a JSON object containing an impersonated refresh token'
475
+ ) ;
476
+ }
477
+ if ( json . type !== IMPERSONATED_ACCOUNT_TYPE ) {
478
+ throw new Error (
479
+ `The incoming JSON object does not have the "${ IMPERSONATED_ACCOUNT_TYPE } " type`
480
+ ) ;
481
+ }
482
+ if ( ! json . source_credentials ) {
483
+ throw new Error (
484
+ 'The incoming JSON object does not contain a source_credentials field'
485
+ ) ;
486
+ }
487
+ if ( ! json . service_account_impersonation_url ) {
488
+ throw new Error (
489
+ 'The incoming JSON object does not contain a service_account_impersonation_url field'
490
+ ) ;
491
+ }
492
+
493
+ // Create source client for impersonation
494
+ const sourceClient = new UserRefreshClient (
495
+ json . source_credentials . client_id ,
496
+ json . source_credentials . client_secret ,
497
+ json . source_credentials . refresh_token
498
+ ) ;
499
+
500
+ // Extreact service account from service_account_impersonation_url
501
+ const targetPrincipal = / (?< target > [ ^ / ] + ) : g e n e r a t e A c c e s s T o k e n $ / . exec (
502
+ json . service_account_impersonation_url
503
+ ) ?. groups ?. target ;
504
+
505
+ if ( ! targetPrincipal ) {
506
+ throw new RangeError (
507
+ `Cannot extract target principal from ${ json . service_account_impersonation_url } `
508
+ ) ;
509
+ }
510
+
511
+ const targetScopes = this . getAnyScopes ( ) ?? [ ] ;
512
+
513
+ const client = new Impersonated ( {
514
+ delegates : json . delegates ?? [ ] ,
515
+ sourceClient : sourceClient ,
516
+ targetPrincipal : targetPrincipal ,
517
+ targetScopes : Array . isArray ( targetScopes ) ? targetScopes : [ targetScopes ] ,
518
+ } ) ;
519
+ return client ;
520
+ }
521
+
462
522
/**
463
523
* Create a credentials instance using the given input options.
464
524
* @param json The input object.
465
525
* @param options The JWT or UserRefresh options for the client
466
526
* @returns JWT or UserRefresh Client with data
467
527
*/
468
- fromJSON ( json : JWTInput , options ?: RefreshOptions ) : JSONClient {
528
+ fromJSON (
529
+ json : JWTInput | ImpersonatedJWTInput ,
530
+ options ?: RefreshOptions
531
+ ) : JSONClient {
469
532
let client : JSONClient ;
470
533
if ( ! json ) {
471
534
throw new Error (
@@ -476,6 +539,8 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
476
539
if ( json . type === 'authorized_user' ) {
477
540
client = new UserRefreshClient ( options ) ;
478
541
client . fromJSON ( json ) ;
542
+ } else if ( json . type === IMPERSONATED_ACCOUNT_TYPE ) {
543
+ client = this . fromImpersonatedJSON ( json as ImpersonatedJWTInput ) ;
479
544
} else if ( json . type === EXTERNAL_ACCOUNT_TYPE ) {
480
545
client = ExternalAccountClient . fromJSON (
481
546
json as ExternalAccountClientOptions ,
@@ -508,6 +573,8 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
508
573
if ( json . type === 'authorized_user' ) {
509
574
client = new UserRefreshClient ( options ) ;
510
575
client . fromJSON ( json ) ;
576
+ } else if ( json . type === IMPERSONATED_ACCOUNT_TYPE ) {
577
+ client = this . fromImpersonatedJSON ( json as ImpersonatedJWTInput ) ;
511
578
} else if ( json . type === EXTERNAL_ACCOUNT_TYPE ) {
512
579
client = ExternalAccountClient . fromJSON (
513
580
json as ExternalAccountClientOptions ,
0 commit comments