11'use strict' ;
22
3- import https , { RequestOptions } from 'https ' ;
4- import { URL , URLSearchParams } from 'url' ;
3+ import { HttpClient } from '@actions/http-client ' ;
4+ import { URLSearchParams } from 'url' ;
55import {
66 GoogleAccessTokenParameters ,
77 GoogleAccessTokenResponse ,
@@ -13,85 +13,53 @@ import {
1313// eslint-disable-next-line @typescript-eslint/no-var-requires
1414const { version : appVersion } = require ( '../package.json' ) ;
1515
16+ // userAgent is the default user agent.
17+ const userAgent = `google-github-actions:auth/${ appVersion } ` ;
18+
19+ /**
20+ * BaseClient is the default HTTP client for interacting with the IAM
21+ * credentials API.
22+ */
1623export class BaseClient {
1724 /**
18- * request is a high-level helper that returns a promise from the executed
19- * request.
25+ * client is the HTTP client.
2026 */
21- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
22- static request ( opts : RequestOptions , data ?: any ) : Promise < string > {
23- if ( ! opts . headers ) {
24- opts . headers = { } ;
25- }
26-
27- if ( ! opts . headers [ 'User-Agent' ] ) {
28- opts . headers [ 'User-Agent' ] = `google-github-actions:auth/${ appVersion } ` ;
29- }
30-
31- return new Promise ( ( resolve , reject ) => {
32- const req = https . request ( opts , ( res ) => {
33- res . setEncoding ( 'utf8' ) ;
34-
35- let body = '' ;
36- res . on ( 'data' , ( data ) => {
37- body += data ;
38- } ) ;
39-
40- res . on ( 'end' , ( ) => {
41- if ( res . statusCode && res . statusCode >= 400 ) {
42- reject ( body ) ;
43- } else {
44- resolve ( body ) ;
45- }
46- } ) ;
47- } ) ;
48-
49- req . on ( 'error' , ( err ) => {
50- reject ( err ) ;
51- } ) ;
52-
53- if ( data != null ) {
54- req . write ( data ) ;
55- }
27+ protected readonly client : HttpClient ;
5628
57- req . end ( ) ;
58- } ) ;
29+ constructor ( ) {
30+ this . client = new HttpClient ( userAgent ) ;
5931 }
6032
6133 /**
6234 * googleIDToken generates a Google Cloud ID token for the provided
6335 * service account email or unique id.
6436 */
65- static async googleIDToken (
37+ async googleIDToken (
6638 token : string ,
6739 { serviceAccount, audience, delegates, includeEmail } : GoogleIDTokenParameters ,
6840 ) : Promise < GoogleIDTokenResponse > {
69- const serviceAccountID = `projects/-/serviceAccounts/${ serviceAccount } ` ;
70- const tokenURL = new URL (
71- `https://iamcredentials.googleapis.com/v1/${ serviceAccountID } :generateIdToken` ,
72- ) ;
41+ const pth = `https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${ serviceAccount } :generateIdToken` ;
7342
7443 const data = {
7544 delegates : delegates ,
7645 audience : audience ,
7746 includeEmail : includeEmail ,
7847 } ;
7948
80- const opts = {
81- hostname : tokenURL . hostname ,
82- port : tokenURL . port ,
83- path : tokenURL . pathname + tokenURL . search ,
84- method : 'POST' ,
85- headers : {
86- 'Authorization' : `Bearer ${ token } ` ,
87- 'Accept' : 'application/json' ,
88- 'Content-Type' : 'application/json' ,
89- } ,
49+ const headers = {
50+ 'Authorization' : `Bearer ${ token } ` ,
51+ 'Accept' : 'application/json' ,
52+ 'Content-Type' : 'application/json' ,
9053 } ;
9154
9255 try {
93- const resp = await BaseClient . request ( opts , JSON . stringify ( data ) ) ;
94- const parsed = JSON . parse ( resp ) ;
56+ const resp = await this . client . request ( 'POST' , pth , JSON . stringify ( data ) , headers ) ;
57+ const body = await resp . readBody ( ) ;
58+ const statusCode = resp . message . statusCode || 500 ;
59+ if ( statusCode >= 400 ) {
60+ throw new Error ( `(${ statusCode } ) ${ body } ` ) ;
61+ }
62+ const parsed = JSON . parse ( body ) ;
9563 return {
9664 token : parsed [ 'token' ] ,
9765 } ;
@@ -104,14 +72,11 @@ export class BaseClient {
10472 * googleAccessToken generates a Google Cloud access token for the provided
10573 * service account email or unique id.
10674 */
107- static async googleAccessToken (
75+ async googleAccessToken (
10876 token : string ,
10977 { serviceAccount, delegates, scopes, lifetime } : GoogleAccessTokenParameters ,
11078 ) : Promise < GoogleAccessTokenResponse > {
111- const serviceAccountID = `projects/-/serviceAccounts/${ serviceAccount } ` ;
112- const tokenURL = new URL (
113- `https://iamcredentials.googleapis.com/v1/${ serviceAccountID } :generateAccessToken` ,
114- ) ;
79+ const pth = `https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${ serviceAccount } :generateAccessToken` ;
11580
11681 const data : Record < string , string | Array < string > > = { } ;
11782 if ( delegates && delegates . length > 0 ) {
@@ -125,21 +90,20 @@ export class BaseClient {
12590 data . lifetime = `${ lifetime } s` ;
12691 }
12792
128- const opts = {
129- hostname : tokenURL . hostname ,
130- port : tokenURL . port ,
131- path : tokenURL . pathname + tokenURL . search ,
132- method : 'POST' ,
133- headers : {
134- 'Authorization' : `Bearer ${ token } ` ,
135- 'Accept' : 'application/json' ,
136- 'Content-Type' : 'application/json' ,
137- } ,
93+ const headers = {
94+ 'Authorization' : `Bearer ${ token } ` ,
95+ 'Accept' : 'application/json' ,
96+ 'Content-Type' : 'application/json' ,
13897 } ;
13998
14099 try {
141- const resp = await BaseClient . request ( opts , JSON . stringify ( data ) ) ;
142- const parsed = JSON . parse ( resp ) ;
100+ const resp = await this . client . request ( 'POST' , pth , JSON . stringify ( data ) , headers ) ;
101+ const body = await resp . readBody ( ) ;
102+ const statusCode = resp . message . statusCode || 500 ;
103+ if ( statusCode >= 400 ) {
104+ throw new Error ( `(${ statusCode } ) ${ body } ` ) ;
105+ }
106+ const parsed = JSON . parse ( body ) ;
143107 return {
144108 accessToken : parsed [ 'accessToken' ] ,
145109 expiration : parsed [ 'expireTime' ] ,
@@ -155,27 +119,26 @@ export class BaseClient {
155119 *
156120 * @param assertion A signed JWT.
157121 */
158- static async googleOAuthToken ( assertion : string ) : Promise < GoogleAccessTokenResponse > {
159- const tokenURL = new URL ( 'https://oauth2.googleapis.com/token' ) ;
160-
161- const opts = {
162- hostname : tokenURL . hostname ,
163- port : tokenURL . port ,
164- path : tokenURL . pathname + tokenURL . search ,
165- method : 'POST' ,
166- headers : {
167- 'Accept' : 'application/json' ,
168- 'Content-Type' : 'application/x-www-form-urlencoded' ,
169- } ,
122+ async googleOAuthToken ( assertion : string ) : Promise < GoogleAccessTokenResponse > {
123+ const pth = `https://oauth2.googleapis.com/token` ;
124+
125+ const headers = {
126+ 'Accept' : 'application/json' ,
127+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
170128 } ;
171129
172130 const data = new URLSearchParams ( ) ;
173131 data . append ( 'grant_type' , 'urn:ietf:params:oauth:grant-type:jwt-bearer' ) ;
174132 data . append ( 'assertion' , assertion ) ;
175133
176134 try {
177- const resp = await BaseClient . request ( opts , data . toString ( ) ) ;
178- const parsed = JSON . parse ( resp ) ;
135+ const resp = await this . client . request ( 'POST' , pth , data . toString ( ) , headers ) ;
136+ const body = await resp . readBody ( ) ;
137+ const statusCode = resp . message . statusCode || 500 ;
138+ if ( statusCode >= 400 ) {
139+ throw new Error ( `(${ statusCode } ) ${ body } ` ) ;
140+ }
141+ const parsed = JSON . parse ( body ) ;
179142
180143 // Normalize the expiration to be a timestamp like the iamcredentials API.
181144 // This API returns the number of seconds until expiration, so convert
0 commit comments