@@ -20,12 +20,12 @@ import os = require('os');
2020import path = require( 'path' ) ;
2121
2222import { AppErrorCodes , FirebaseAppError } from '../utils/error' ;
23+ import { HttpClient , HttpRequestConfig } from '../utils/api-request' ;
2324
2425
2526const GOOGLE_TOKEN_AUDIENCE = 'https://accounts.google.com/o/oauth2/token' ;
2627const GOOGLE_AUTH_TOKEN_HOST = 'accounts.google.com' ;
2728const GOOGLE_AUTH_TOKEN_PATH = '/o/oauth2/token' ;
28- const GOOGLE_AUTH_TOKEN_PORT = 443 ;
2929
3030// NOTE: the Google Metadata Service uses HTTP over a vlan
3131const GOOGLE_METADATA_SERVICE_HOST = 'metadata.google.internal' ;
@@ -46,7 +46,6 @@ const GCLOUD_CREDENTIAL_SUFFIX = 'gcloud/application_default_credentials.json';
4646const GCLOUD_CREDENTIAL_PATH = configDir && path . resolve ( configDir , GCLOUD_CREDENTIAL_SUFFIX ) ;
4747
4848const REFRESH_TOKEN_HOST = 'www.googleapis.com' ;
49- const REFRESH_TOKEN_PORT = 443 ;
5049const REFRESH_TOKEN_PATH = '/oauth2/v4/token' ;
5150
5251const ONE_HOUR_IN_SECONDS = 60 * 60 ;
@@ -186,79 +185,63 @@ export interface GoogleOAuthAccessToken {
186185}
187186
188187/**
189- * A wrapper around the http and https request libraries to simplify & promisify JSON requests.
190- * TODO(inlined): Create a type for "transit".
188+ * Obtain a new OAuth2 token by making a remote service call.
191189 */
192- function requestAccessToken ( transit , options : object , data ?: any ) : Promise < GoogleOAuthAccessToken > {
193- return new Promise ( ( resolve , reject ) => {
194- const req = transit . request ( options , ( res ) => {
195- const buffers : Buffer [ ] = [ ] ;
196- res . on ( 'data' , ( buffer ) => buffers . push ( buffer ) ) ;
197- res . on ( 'end' , ( ) => {
198- try {
199- const json = JSON . parse ( Buffer . concat ( buffers ) . toString ( ) ) ;
200- if ( json . error ) {
201- let errorMessage = 'Error fetching access token: ' + json . error ;
202- if ( json . error_description ) {
203- errorMessage += ' (' + json . error_description + ')' ;
204- }
205- reject ( new FirebaseAppError ( AppErrorCodes . INVALID_CREDENTIAL , errorMessage ) ) ;
206- } else if ( ! json . access_token || ! json . expires_in ) {
207- reject ( new FirebaseAppError (
208- AppErrorCodes . INVALID_CREDENTIAL ,
209- `Unexpected response while fetching access token: ${ JSON . stringify ( json ) } ` ,
210- ) ) ;
211- } else {
212- resolve ( json ) ;
213- }
214- } catch ( err ) {
215- reject ( new FirebaseAppError (
216- AppErrorCodes . INVALID_CREDENTIAL ,
217- `Failed to parse access token response: ${ err . toString ( ) } ` ,
218- ) ) ;
219- }
220- } ) ;
221- } ) ;
222- req . on ( 'error' , reject ) ;
223- if ( data ) {
224- req . write ( data ) ;
190+ function requestAccessToken ( client : HttpClient , request : HttpRequestConfig ) : Promise < GoogleOAuthAccessToken > {
191+ return client . send ( request ) . then ( ( resp ) => {
192+ const json = resp . data ;
193+ if ( json . error ) {
194+ let errorMessage = 'Error fetching access token: ' + json . error ;
195+ if ( json . error_description ) {
196+ errorMessage += ' (' + json . error_description + ')' ;
197+ }
198+ throw new FirebaseAppError ( AppErrorCodes . INVALID_CREDENTIAL , errorMessage ) ;
199+ } else if ( ! json . access_token || ! json . expires_in ) {
200+ throw new FirebaseAppError (
201+ AppErrorCodes . INVALID_CREDENTIAL ,
202+ `Unexpected response while fetching access token: ${ JSON . stringify ( json ) } ` ,
203+ ) ;
204+ } else {
205+ return json ;
225206 }
226- req . end ( ) ;
207+ } ) . catch ( ( err ) => {
208+ throw new FirebaseAppError (
209+ AppErrorCodes . INVALID_CREDENTIAL ,
210+ `Failed to parse access token response: ${ err . toString ( ) } ` ,
211+ ) ;
227212 } ) ;
228213}
229214
230215/**
231216 * Implementation of Credential that uses a service account certificate.
232217 */
233218export class CertCredential implements Credential {
234- private certificate_ : Certificate ;
219+ private readonly certificate : Certificate ;
220+ private readonly httpClient : HttpClient ;
235221
236222 constructor ( serviceAccountPathOrObject : string | object ) {
237- this . certificate_ = ( typeof serviceAccountPathOrObject === 'string' ) ?
223+ this . certificate = ( typeof serviceAccountPathOrObject === 'string' ) ?
238224 Certificate . fromPath ( serviceAccountPathOrObject ) : new Certificate ( serviceAccountPathOrObject ) ;
225+ this . httpClient = new HttpClient ( ) ;
239226 }
240227
241228 public getAccessToken ( ) : Promise < GoogleOAuthAccessToken > {
242229 const token = this . createAuthJwt_ ( ) ;
243230 const postData = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3A' +
244- 'grant-type%3Ajwt-bearer&assertion=' +
245- token ;
246- const options = {
231+ 'grant-type%3Ajwt-bearer&assertion=' + token ;
232+ const request : HttpRequestConfig = {
247233 method : 'POST' ,
248- host : GOOGLE_AUTH_TOKEN_HOST ,
249- port : GOOGLE_AUTH_TOKEN_PORT ,
250- path : GOOGLE_AUTH_TOKEN_PATH ,
234+ url : `https://${ GOOGLE_AUTH_TOKEN_HOST } ${ GOOGLE_AUTH_TOKEN_PATH } ` ,
251235 headers : {
252236 'Content-Type' : 'application/x-www-form-urlencoded' ,
253- 'Content-Length' : postData . length ,
254237 } ,
238+ data : postData ,
255239 } ;
256- const https = require ( 'https' ) ;
257- return requestAccessToken ( https , options , postData ) ;
240+ return requestAccessToken ( this . httpClient , request ) ;
258241 }
259242
260243 public getCertificate ( ) : Certificate {
261- return this . certificate_ ;
244+ return this . certificate ;
262245 }
263246
264247 private createAuthJwt_ ( ) : string {
@@ -274,10 +257,10 @@ export class CertCredential implements Credential {
274257
275258 const jwt = require ( 'jsonwebtoken' ) ;
276259 // This method is actually synchronous so we can capture and return the buffer.
277- return jwt . sign ( claims , this . certificate_ . privateKey , {
260+ return jwt . sign ( claims , this . certificate . privateKey , {
278261 audience : GOOGLE_TOKEN_AUDIENCE ,
279262 expiresIn : ONE_HOUR_IN_SECONDS ,
280- issuer : this . certificate_ . clientEmail ,
263+ issuer : this . certificate . clientEmail ,
281264 algorithm : JWT_ALGORITHM ,
282265 } ) ;
283266 }
@@ -295,32 +278,30 @@ export interface Credential {
295278 * Implementation of Credential that gets access tokens from refresh tokens.
296279 */
297280export class RefreshTokenCredential implements Credential {
298- private refreshToken_ : RefreshToken ;
281+ private readonly refreshToken : RefreshToken ;
282+ private readonly httpClient : HttpClient ;
299283
300284 constructor ( refreshTokenPathOrObject : string | object ) {
301- this . refreshToken_ = ( typeof refreshTokenPathOrObject === 'string' ) ?
285+ this . refreshToken = ( typeof refreshTokenPathOrObject === 'string' ) ?
302286 RefreshToken . fromPath ( refreshTokenPathOrObject ) : new RefreshToken ( refreshTokenPathOrObject ) ;
287+ this . httpClient = new HttpClient ( ) ;
303288 }
304289
305290 public getAccessToken ( ) : Promise < GoogleOAuthAccessToken > {
306291 const postData =
307- 'client_id=' + this . refreshToken_ . clientId + '&' +
308- 'client_secret=' + this . refreshToken_ . clientSecret + '&' +
309- 'refresh_token=' + this . refreshToken_ . refreshToken + '&' +
292+ 'client_id=' + this . refreshToken . clientId + '&' +
293+ 'client_secret=' + this . refreshToken . clientSecret + '&' +
294+ 'refresh_token=' + this . refreshToken . refreshToken + '&' +
310295 'grant_type=refresh_token' ;
311-
312- const options = {
296+ const request : HttpRequestConfig = {
313297 method : 'POST' ,
314- host : REFRESH_TOKEN_HOST ,
315- port : REFRESH_TOKEN_PORT ,
316- path : REFRESH_TOKEN_PATH ,
298+ url : `https://${ REFRESH_TOKEN_HOST } ${ REFRESH_TOKEN_PATH } ` ,
317299 headers : {
318300 'Content-Type' : 'application/x-www-form-urlencoded' ,
319- 'Content-Length' : postData . length ,
320301 } ,
302+ data : postData ,
321303 } ;
322- const https = require ( 'https' ) ;
323- return requestAccessToken ( https , options , postData ) ;
304+ return requestAccessToken ( this . httpClient , request ) ;
324305 }
325306
326307 public getCertificate ( ) : Certificate {
@@ -335,17 +316,15 @@ export class RefreshTokenCredential implements Credential {
335316 * of an App Engine instance or Google Compute Engine machine.
336317 */
337318export class MetadataServiceCredential implements Credential {
319+
320+ private readonly httpClient = new HttpClient ( ) ;
321+
338322 public getAccessToken ( ) : Promise < GoogleOAuthAccessToken > {
339- const options = {
323+ const request : HttpRequestConfig = {
340324 method : 'GET' ,
341- host : GOOGLE_METADATA_SERVICE_HOST ,
342- path : GOOGLE_METADATA_SERVICE_PATH ,
343- headers : {
344- 'Content-Length' : 0 ,
345- } ,
325+ url : `http://${ GOOGLE_METADATA_SERVICE_HOST } ${ GOOGLE_METADATA_SERVICE_PATH } ` ,
346326 } ;
347- const http = require ( 'http' ) ;
348- return requestAccessToken ( http , options ) ;
327+ return requestAccessToken ( this . httpClient , request ) ;
349328 }
350329
351330 public getCertificate ( ) : Certificate {
0 commit comments