Skip to content

Commit 8b92183

Browse files
authored
fix(auth): Using the App-level http.Agent when fetching public key certificates (#705)
* Using the App-level http.Agent when fetching public certs * Reverted package lock
1 parent ef231fb commit 8b92183

File tree

4 files changed

+52
-33
lines changed

4 files changed

+52
-33
lines changed

src/auth/auth-api-request.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,13 @@ export abstract class AbstractAuthRequestHandler {
701701
* @constructor
702702
*/
703703
constructor(app: FirebaseApp) {
704+
if (typeof app !== 'object' || app === null || !('options' in app)) {
705+
throw new FirebaseAuthError(
706+
AuthClientErrorCode.INVALID_ARGUMENT,
707+
'First argument passed to admin.auth() must be a valid Firebase app instance.',
708+
);
709+
}
710+
704711
this.projectId = utils.getProjectId(app);
705712
this.httpClient = new AuthorizedHttpClient(app);
706713
}

src/auth/auth.ts

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import {UserRecord, CreateRequest, UpdateRequest} from './user-record';
1818
import {FirebaseApp} from '../firebase-app';
19-
import {FirebaseTokenGenerator, CryptoSigner, cryptoSignerFromApp} from './token-generator';
19+
import {FirebaseTokenGenerator, cryptoSignerFromApp} from './token-generator';
2020
import {
2121
AbstractAuthRequestHandler, AuthRequestHandler, TenantAwareAuthRequestHandler,
2222
} from './auth-api-request';
@@ -90,6 +90,7 @@ export interface SessionCookieOptions {
9090
* Base Auth class. Mainly used for user management APIs.
9191
*/
9292
export class BaseAuth<T extends AbstractAuthRequestHandler> {
93+
9394
protected readonly tokenGenerator: FirebaseTokenGenerator;
9495
protected readonly idTokenVerifier: FirebaseTokenVerifier;
9596
protected readonly sessionCookieVerifier: FirebaseTokenVerifier;
@@ -104,12 +105,14 @@ export class BaseAuth<T extends AbstractAuthRequestHandler> {
104105
* minting.
105106
* @constructor
106107
*/
107-
constructor(protected readonly projectId: string,
108-
protected readonly authRequestHandler: T,
109-
cryptoSigner: CryptoSigner) {
108+
constructor(app: FirebaseApp, protected readonly authRequestHandler: T) {
109+
const cryptoSigner = cryptoSignerFromApp(app);
110110
this.tokenGenerator = new FirebaseTokenGenerator(cryptoSigner);
111-
this.sessionCookieVerifier = createSessionCookieVerifier(projectId);
112-
this.idTokenVerifier = createIdTokenVerifier(projectId);
111+
112+
const projectId = utils.getProjectId(app);
113+
const httpAgent = app.options.httpAgent;
114+
this.sessionCookieVerifier = createSessionCookieVerifier(projectId, httpAgent);
115+
this.idTokenVerifier = createIdTokenVerifier(projectId, httpAgent);
113116
}
114117

115118
/**
@@ -617,10 +620,7 @@ export class TenantAwareAuth extends BaseAuth<TenantAwareAuthRequestHandler> {
617620
* @constructor
618621
*/
619622
constructor(app: FirebaseApp, tenantId: string) {
620-
super(
621-
utils.getProjectId(app),
622-
new TenantAwareAuthRequestHandler(app, tenantId),
623-
cryptoSignerFromApp(app));
623+
super(app, new TenantAwareAuthRequestHandler(app, tenantId));
624624
utils.addReadonlyGetter(this, 'tenantId', tenantId);
625625
}
626626

@@ -721,35 +721,17 @@ export class TenantAwareAuth extends BaseAuth<TenantAwareAuthRequestHandler> {
721721
* An Auth instance can have multiple tenants.
722722
*/
723723
export class Auth extends BaseAuth<AuthRequestHandler> implements FirebaseServiceInterface {
724+
724725
public INTERNAL: AuthInternals = new AuthInternals();
725726
private readonly tenantManager_: TenantManager;
726727
private readonly app_: FirebaseApp;
727728

728-
/**
729-
* Returns the FirebaseApp's project ID.
730-
*
731-
* @param {FirebaseApp} app The project ID for an app.
732-
* @return {string} The FirebaseApp's project ID.
733-
*/
734-
private static getProjectId(app: FirebaseApp): string {
735-
if (typeof app !== 'object' || app === null || !('options' in app)) {
736-
throw new FirebaseAuthError(
737-
AuthClientErrorCode.INVALID_ARGUMENT,
738-
'First argument passed to admin.auth() must be a valid Firebase app instance.',
739-
);
740-
}
741-
return utils.getProjectId(app);
742-
}
743-
744729
/**
745730
* @param {object} app The app for this Auth service.
746731
* @constructor
747732
*/
748733
constructor(app: FirebaseApp) {
749-
super(
750-
Auth.getProjectId(app),
751-
new AuthRequestHandler(app),
752-
cryptoSignerFromApp(app));
734+
super(app, new AuthRequestHandler(app));
753735
this.app_ = app;
754736
this.tenantManager_ = new TenantManager(app);
755737
}

src/auth/token-verifier.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import * as validator from '../utils/validator';
2020
import * as jwt from 'jsonwebtoken';
2121
import { HttpClient, HttpRequestConfig, HttpError } from '../utils/api-request';
2222
import { DecodedIdToken } from './auth';
23+
import { Agent } from 'http';
2324

2425
// Audience to use for Firebase Auth Custom tokens
2526
const FIREBASE_AUDIENCE = 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit';
@@ -75,7 +76,8 @@ export class FirebaseTokenVerifier {
7576

7677
constructor(private clientCertUrl: string, private algorithm: string,
7778
private issuer: string, private projectId: string,
78-
private tokenInfo: FirebaseTokenInfo) {
79+
private tokenInfo: FirebaseTokenInfo,
80+
private readonly httpAgent?: Agent) {
7981
if (!validator.isURL(clientCertUrl)) {
8082
throw new FirebaseAuthError(
8183
AuthClientErrorCode.INVALID_ARGUMENT,
@@ -282,6 +284,7 @@ export class FirebaseTokenVerifier {
282284
const request: HttpRequestConfig = {
283285
method: 'GET',
284286
url: this.clientCertUrl,
287+
httpAgent: this.httpAgent,
285288
};
286289
return client.send(request).then((resp) => {
287290
if (!resp.isJson() || resp.data.error) {
@@ -325,30 +328,34 @@ export class FirebaseTokenVerifier {
325328
* Creates a new FirebaseTokenVerifier to verify Firebase ID tokens.
326329
*
327330
* @param {string} projectId Project ID string.
331+
* @param {Agent} httpAgent Optional HTTP agent.
328332
* @return {FirebaseTokenVerifier}
329333
*/
330-
export function createIdTokenVerifier(projectId: string): FirebaseTokenVerifier {
334+
export function createIdTokenVerifier(projectId: string, httpAgent?: Agent): FirebaseTokenVerifier {
331335
return new FirebaseTokenVerifier(
332336
CLIENT_CERT_URL,
333337
ALGORITHM_RS256,
334338
'https://securetoken.google.com/',
335339
projectId,
336340
ID_TOKEN_INFO,
341+
httpAgent,
337342
);
338343
}
339344

340345
/**
341346
* Creates a new FirebaseTokenVerifier to verify Firebase session cookies.
342347
*
343348
* @param {string} projectId Project ID string.
349+
* @param {Agent} httpAgent Optional HTTP agent.
344350
* @return {FirebaseTokenVerifier}
345351
*/
346-
export function createSessionCookieVerifier(projectId: string): FirebaseTokenVerifier {
352+
export function createSessionCookieVerifier(projectId: string, httpAgent?: Agent): FirebaseTokenVerifier {
347353
return new FirebaseTokenVerifier(
348354
SESSION_COOKIE_CERT_URL,
349355
ALGORITHM_RS256,
350356
'https://session.firebase.google.com/',
351357
projectId,
352358
SESSION_COOKIE_INFO,
359+
httpAgent,
353360
);
354361
}

test/unit/auth/token-verifier.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,29 @@ describe('FirebaseTokenVerifier', () => {
527527
});
528528
});
529529

530+
it('should use the given HTTP Agent', () => {
531+
const agent = new https.Agent();
532+
tokenVerifier = new verifier.FirebaseTokenVerifier(
533+
'https://www.googleapis.com/robot/v1/metadata/x509/[email protected]',
534+
'RS256',
535+
'https://securetoken.google.com/',
536+
'project_id',
537+
verifier.ID_TOKEN_INFO,
538+
agent,
539+
);
540+
mockedRequests.push(mockFetchPublicKeys());
541+
542+
clock = sinon.useFakeTimers(1000);
543+
544+
const mockIdToken = mocks.generateIdToken();
545+
546+
return tokenVerifier.verifyJWT(mockIdToken)
547+
.then(() => {
548+
expect(https.request).to.have.been.calledOnce;
549+
expect(httpsSpy.args[0][0].agent).to.equal(agent);
550+
});
551+
});
552+
530553
it('should not fetch the Google cert public keys until the first time verifyJWT() is called', () => {
531554
mockedRequests.push(mockFetchPublicKeys());
532555

0 commit comments

Comments
 (0)