@@ -5,14 +5,16 @@ import {
55 JwtError ,
66 JwtErrorCode ,
77} from "./errors" ;
8- import { PublicKeySignatureVerifier , SignatureVerifier } from "./jws-verifier" ;
8+ import { EmulatorSignatureVerifier , PublicKeySignatureVerifier , SignatureVerifier } from "./jws-verifier" ;
99import { DecodedPayload , RS256Token } from "./jwt-decoder" ;
10- import * as validator from "./validator" ;
10+ import { isNonEmptyString , isNonNullObject , isString , isURL } from "./validator" ;
1111
1212// Audience to use for Firebase Auth Custom tokens
13- const FIREBASE_AUDIENCE =
13+ export const FIREBASE_AUDIENCE =
1414 "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" ;
1515
16+ const EMULATOR_VERIFIER = new EmulatorSignatureVerifier ( )
17+
1618/**
1719 * Interface representing a decoded Firebase ID token, returned from the
1820 * {@link BaseAuth.verifyIdToken} method.
@@ -174,57 +176,50 @@ const makeExpectedbutGotMsg = (want: any, got: any) =>
174176 */
175177export class FirebaseTokenVerifier {
176178 private readonly shortNameArticle : string ;
177- private readonly signatureVerifier : SignatureVerifier ;
178179
179180 constructor (
180- clientCertUrl : string ,
181- cfKVNamespace : KVNamespace ,
181+ private readonly signatureVerifier : SignatureVerifier ,
182182 private projectId : string ,
183183 private issuer : string ,
184184 private tokenInfo : FirebaseTokenInfo
185185 ) {
186- if ( ! validator . isURL ( clientCertUrl ) ) {
187- throw new FirebaseAuthError (
188- AuthClientErrorCode . INVALID_ARGUMENT ,
189- "The provided public client certificate URL is an invalid URL."
190- ) ;
191- } else if ( ! validator . isNonEmptyString ( projectId ) ) {
186+ if ( ! isNonEmptyString ( projectId ) ) {
192187 throw new FirebaseAuthError (
193188 AuthClientErrorCode . INVALID_ARGUMENT ,
194189 "Your Firebase project ID must be a non-empty string"
195190 ) ;
196- } else if ( ! validator . isURL ( issuer ) ) {
191+ } else if ( ! isURL ( issuer ) ) {
197192 throw new FirebaseAuthError (
198193 AuthClientErrorCode . INVALID_ARGUMENT ,
199194 "The provided JWT issuer is an invalid URL."
200195 ) ;
201- } else if ( ! validator . isNonNullObject ( tokenInfo ) ) {
196+ } else if ( ! isNonNullObject ( tokenInfo ) ) {
202197 throw new FirebaseAuthError (
203198 AuthClientErrorCode . INVALID_ARGUMENT ,
204199 "The provided JWT information is not an object or null."
205200 ) ;
206- } else if ( ! validator . isURL ( tokenInfo . url ) ) {
201+ } else if ( ! isURL ( tokenInfo . url ) ) {
207202 throw new FirebaseAuthError (
208203 AuthClientErrorCode . INVALID_ARGUMENT ,
209204 "The provided JWT verification documentation URL is invalid."
210205 ) ;
211- } else if ( ! validator . isNonEmptyString ( tokenInfo . verifyApiName ) ) {
206+ } else if ( ! isNonEmptyString ( tokenInfo . verifyApiName ) ) {
212207 throw new FirebaseAuthError (
213208 AuthClientErrorCode . INVALID_ARGUMENT ,
214209 "The JWT verify API name must be a non-empty string."
215210 ) ;
216- } else if ( ! validator . isNonEmptyString ( tokenInfo . jwtName ) ) {
211+ } else if ( ! isNonEmptyString ( tokenInfo . jwtName ) ) {
217212 throw new FirebaseAuthError (
218213 AuthClientErrorCode . INVALID_ARGUMENT ,
219214 "The JWT public full name must be a non-empty string."
220215 ) ;
221- } else if ( ! validator . isNonEmptyString ( tokenInfo . shortName ) ) {
216+ } else if ( ! isNonEmptyString ( tokenInfo . shortName ) ) {
222217 throw new FirebaseAuthError (
223218 AuthClientErrorCode . INVALID_ARGUMENT ,
224219 "The JWT public short name must be a non-empty string."
225220 ) ;
226221 } else if (
227- ! validator . isNonNullObject ( tokenInfo . expiredErrorCode ) ||
222+ ! isNonNullObject ( tokenInfo . expiredErrorCode ) ||
228223 ! ( "code" in tokenInfo . expiredErrorCode )
229224 ) {
230225 throw new FirebaseAuthError (
@@ -235,11 +230,6 @@ export class FirebaseTokenVerifier {
235230 this . shortNameArticle = tokenInfo . shortName . charAt ( 0 ) . match ( / [ a e i o u ] / i)
236231 ? "an"
237232 : "a" ;
238-
239- this . signatureVerifier = PublicKeySignatureVerifier . withCertificateUrl (
240- clientCertUrl ,
241- cfKVNamespace
242- ) ;
243233 }
244234
245235 /**
@@ -253,13 +243,13 @@ export class FirebaseTokenVerifier {
253243 jwtToken : string ,
254244 isEmulator = false
255245 ) : Promise < FirebaseIdToken > {
256- if ( ! validator . isString ( jwtToken ) ) {
246+ if ( ! isString ( jwtToken ) ) {
257247 throw new FirebaseAuthError (
258248 AuthClientErrorCode . INVALID_ARGUMENT ,
259249 `First argument to ${ this . tokenInfo . verifyApiName } must be a ${ this . tokenInfo . jwtName } string.`
260250 ) ;
261251 }
262- return this . decodeAndVerify ( jwtToken , this . projectId , isEmulator ) . then (
252+ return this . decodeAndVerify ( jwtToken , isEmulator ) . then (
263253 ( payload ) => {
264254 payload . uid = payload . sub ;
265255 return payload ;
@@ -269,15 +259,14 @@ export class FirebaseTokenVerifier {
269259
270260 private async decodeAndVerify (
271261 token : string ,
272- projectId : string ,
273262 isEmulator : boolean
274263 ) : Promise < FirebaseIdToken > {
275264 const currentTimestamp = Math . floor ( Date . now ( ) / 1000 ) ;
276265 try {
277266 const rs256Token = this . safeDecode ( token , isEmulator , currentTimestamp ) ;
278267 const { payload } = rs256Token . decodedToken ;
279268
280- this . verifyPayload ( payload , projectId , currentTimestamp ) ;
269+ this . verifyPayload ( payload , currentTimestamp ) ;
281270 await this . verifySignature ( rs256Token , isEmulator ) ;
282271
283272 return payload ;
@@ -295,9 +284,7 @@ export class FirebaseTokenVerifier {
295284 currentTimestamp : number
296285 ) : RS256Token {
297286 try {
298- return isEmulator
299- ? RS256Token . decode ( jwtToken , currentTimestamp )
300- : RS256Token . decode ( jwtToken , currentTimestamp ) ;
287+ return RS256Token . decode ( jwtToken , currentTimestamp , isEmulator )
301288 } catch ( err ) {
302289 const verifyJwtTokenDocsMessage =
303290 ` See ${ this . tokenInfo . url } ` +
@@ -316,7 +303,6 @@ export class FirebaseTokenVerifier {
316303
317304 private verifyPayload (
318305 tokenPayload : DecodedPayload ,
319- projectId : string ,
320306 currentTimestamp : number
321307 ) : asserts tokenPayload is FirebaseIdToken {
322308 const payload = tokenPayload ;
@@ -331,43 +317,43 @@ export class FirebaseTokenVerifier {
331317 const createInvalidArgument = ( errorMessage : string ) =>
332318 new FirebaseAuthError ( AuthClientErrorCode . INVALID_ARGUMENT , errorMessage ) ;
333319
334- if ( payload . aud !== projectId && payload . aud !== FIREBASE_AUDIENCE ) {
320+ if ( payload . aud !== this . projectId && payload . aud !== FIREBASE_AUDIENCE ) {
335321 throw createInvalidArgument (
336322 `${ this . tokenInfo . jwtName } has incorrect "aud" (audience) claim. ` +
337- makeExpectedbutGotMsg ( projectId , payload . aud ) +
338- projectIdMatchMessage +
339- verifyJwtTokenDocsMessage
323+ makeExpectedbutGotMsg ( this . projectId , payload . aud ) +
324+ projectIdMatchMessage +
325+ verifyJwtTokenDocsMessage
340326 ) ;
341327 }
342328
343- if ( payload . iss !== this . issuer + projectId ) {
329+ if ( payload . iss !== this . issuer + this . projectId ) {
344330 throw createInvalidArgument (
345331 `${ this . tokenInfo . jwtName } has incorrect "iss" (issuer) claim. ` +
346- makeExpectedbutGotMsg ( this . issuer , payload . iss ) +
347- projectIdMatchMessage +
348- verifyJwtTokenDocsMessage
332+ makeExpectedbutGotMsg ( this . issuer , payload . iss ) +
333+ projectIdMatchMessage +
334+ verifyJwtTokenDocsMessage
349335 ) ;
350336 }
351337
352338 if ( payload . sub . length > 128 ) {
353339 throw createInvalidArgument (
354340 `${ this . tokenInfo . jwtName } has "sub" (subject) claim longer than 128 characters.` +
355- verifyJwtTokenDocsMessage
341+ verifyJwtTokenDocsMessage
356342 ) ;
357343 }
358344
359345 // check auth_time claim
360346 if ( typeof payload . auth_time !== "number" ) {
361347 throw createInvalidArgument (
362348 `${ this . tokenInfo . jwtName } has no "auth_time" claim. ` +
363- verifyJwtTokenDocsMessage
349+ verifyJwtTokenDocsMessage
364350 ) ;
365351 }
366352
367353 if ( currentTimestamp < payload . auth_time ) {
368354 throw createInvalidArgument (
369355 `${ this . tokenInfo . jwtName } has incorrect "auth_time" claim. ` +
370- verifyJwtTokenDocsMessage
356+ verifyJwtTokenDocsMessage
371357 ) ;
372358 }
373359 }
@@ -376,9 +362,7 @@ export class FirebaseTokenVerifier {
376362 token : RS256Token ,
377363 isEmulator : boolean
378364 ) : Promise < void > {
379- const verifier = isEmulator
380- ? /* EMULATOR_VERIFIER */ this . signatureVerifier
381- : this . signatureVerifier ;
365+ const verifier = isEmulator ? EMULATOR_VERIFIER : this . signatureVerifier
382366 return await verifier . verify ( token ) ;
383367 }
384368
@@ -466,18 +450,33 @@ export const ID_TOKEN_INFO: FirebaseTokenInfo = {
466450 * Creates a new FirebaseTokenVerifier to verify Firebase ID tokens.
467451 *
468452 * @internal
469- * @param app - Firebase app instance.
470453 * @returns FirebaseTokenVerifier
471454 */
472455export function createIdTokenVerifier (
473456 projectID : string ,
457+ cacheKey : string ,
474458 cfPublicKeyCacheNamespace : KVNamespace
475459) : FirebaseTokenVerifier {
476- return new FirebaseTokenVerifier (
460+ const signatureVerifier = PublicKeySignatureVerifier . withCertificateUrl (
477461 CLIENT_JWK_URL ,
478- cfPublicKeyCacheNamespace ,
462+ cacheKey ,
463+ cfPublicKeyCacheNamespace
464+ ) ;
465+ return createFirebaseTokenVerifier ( signatureVerifier , projectID )
466+ }
467+
468+ /**
469+ * @internal
470+ * @returns FirebaseTokenVerifier
471+ */
472+ export function createFirebaseTokenVerifier (
473+ signatureVerifier : SignatureVerifier ,
474+ projectID : string
475+ ) : FirebaseTokenVerifier {
476+ return new FirebaseTokenVerifier (
477+ signatureVerifier ,
479478 projectID ,
480479 "https://securetoken.google.com/" ,
481480 ID_TOKEN_INFO
482481 ) ;
483- }
482+ }
0 commit comments