@@ -5,14 +5,16 @@ import {
5
5
JwtError ,
6
6
JwtErrorCode ,
7
7
} from "./errors" ;
8
- import { PublicKeySignatureVerifier , SignatureVerifier } from "./jws-verifier" ;
8
+ import { EmulatorSignatureVerifier , PublicKeySignatureVerifier , SignatureVerifier } from "./jws-verifier" ;
9
9
import { DecodedPayload , RS256Token } from "./jwt-decoder" ;
10
- import * as validator from "./validator" ;
10
+ import { isNonEmptyString , isNonNullObject , isString , isURL } from "./validator" ;
11
11
12
12
// Audience to use for Firebase Auth Custom tokens
13
- const FIREBASE_AUDIENCE =
13
+ export const FIREBASE_AUDIENCE =
14
14
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" ;
15
15
16
+ const EMULATOR_VERIFIER = new EmulatorSignatureVerifier ( )
17
+
16
18
/**
17
19
* Interface representing a decoded Firebase ID token, returned from the
18
20
* {@link BaseAuth.verifyIdToken} method.
@@ -174,57 +176,50 @@ const makeExpectedbutGotMsg = (want: any, got: any) =>
174
176
*/
175
177
export class FirebaseTokenVerifier {
176
178
private readonly shortNameArticle : string ;
177
- private readonly signatureVerifier : SignatureVerifier ;
178
179
179
180
constructor (
180
- clientCertUrl : string ,
181
- cfKVNamespace : KVNamespace ,
181
+ private readonly signatureVerifier : SignatureVerifier ,
182
182
private projectId : string ,
183
183
private issuer : string ,
184
184
private tokenInfo : FirebaseTokenInfo
185
185
) {
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 ) ) {
192
187
throw new FirebaseAuthError (
193
188
AuthClientErrorCode . INVALID_ARGUMENT ,
194
189
"Your Firebase project ID must be a non-empty string"
195
190
) ;
196
- } else if ( ! validator . isURL ( issuer ) ) {
191
+ } else if ( ! isURL ( issuer ) ) {
197
192
throw new FirebaseAuthError (
198
193
AuthClientErrorCode . INVALID_ARGUMENT ,
199
194
"The provided JWT issuer is an invalid URL."
200
195
) ;
201
- } else if ( ! validator . isNonNullObject ( tokenInfo ) ) {
196
+ } else if ( ! isNonNullObject ( tokenInfo ) ) {
202
197
throw new FirebaseAuthError (
203
198
AuthClientErrorCode . INVALID_ARGUMENT ,
204
199
"The provided JWT information is not an object or null."
205
200
) ;
206
- } else if ( ! validator . isURL ( tokenInfo . url ) ) {
201
+ } else if ( ! isURL ( tokenInfo . url ) ) {
207
202
throw new FirebaseAuthError (
208
203
AuthClientErrorCode . INVALID_ARGUMENT ,
209
204
"The provided JWT verification documentation URL is invalid."
210
205
) ;
211
- } else if ( ! validator . isNonEmptyString ( tokenInfo . verifyApiName ) ) {
206
+ } else if ( ! isNonEmptyString ( tokenInfo . verifyApiName ) ) {
212
207
throw new FirebaseAuthError (
213
208
AuthClientErrorCode . INVALID_ARGUMENT ,
214
209
"The JWT verify API name must be a non-empty string."
215
210
) ;
216
- } else if ( ! validator . isNonEmptyString ( tokenInfo . jwtName ) ) {
211
+ } else if ( ! isNonEmptyString ( tokenInfo . jwtName ) ) {
217
212
throw new FirebaseAuthError (
218
213
AuthClientErrorCode . INVALID_ARGUMENT ,
219
214
"The JWT public full name must be a non-empty string."
220
215
) ;
221
- } else if ( ! validator . isNonEmptyString ( tokenInfo . shortName ) ) {
216
+ } else if ( ! isNonEmptyString ( tokenInfo . shortName ) ) {
222
217
throw new FirebaseAuthError (
223
218
AuthClientErrorCode . INVALID_ARGUMENT ,
224
219
"The JWT public short name must be a non-empty string."
225
220
) ;
226
221
} else if (
227
- ! validator . isNonNullObject ( tokenInfo . expiredErrorCode ) ||
222
+ ! isNonNullObject ( tokenInfo . expiredErrorCode ) ||
228
223
! ( "code" in tokenInfo . expiredErrorCode )
229
224
) {
230
225
throw new FirebaseAuthError (
@@ -235,11 +230,6 @@ export class FirebaseTokenVerifier {
235
230
this . shortNameArticle = tokenInfo . shortName . charAt ( 0 ) . match ( / [ a e i o u ] / i)
236
231
? "an"
237
232
: "a" ;
238
-
239
- this . signatureVerifier = PublicKeySignatureVerifier . withCertificateUrl (
240
- clientCertUrl ,
241
- cfKVNamespace
242
- ) ;
243
233
}
244
234
245
235
/**
@@ -253,13 +243,13 @@ export class FirebaseTokenVerifier {
253
243
jwtToken : string ,
254
244
isEmulator = false
255
245
) : Promise < FirebaseIdToken > {
256
- if ( ! validator . isString ( jwtToken ) ) {
246
+ if ( ! isString ( jwtToken ) ) {
257
247
throw new FirebaseAuthError (
258
248
AuthClientErrorCode . INVALID_ARGUMENT ,
259
249
`First argument to ${ this . tokenInfo . verifyApiName } must be a ${ this . tokenInfo . jwtName } string.`
260
250
) ;
261
251
}
262
- return this . decodeAndVerify ( jwtToken , this . projectId , isEmulator ) . then (
252
+ return this . decodeAndVerify ( jwtToken , isEmulator ) . then (
263
253
( payload ) => {
264
254
payload . uid = payload . sub ;
265
255
return payload ;
@@ -269,15 +259,14 @@ export class FirebaseTokenVerifier {
269
259
270
260
private async decodeAndVerify (
271
261
token : string ,
272
- projectId : string ,
273
262
isEmulator : boolean
274
263
) : Promise < FirebaseIdToken > {
275
264
const currentTimestamp = Math . floor ( Date . now ( ) / 1000 ) ;
276
265
try {
277
266
const rs256Token = this . safeDecode ( token , isEmulator , currentTimestamp ) ;
278
267
const { payload } = rs256Token . decodedToken ;
279
268
280
- this . verifyPayload ( payload , projectId , currentTimestamp ) ;
269
+ this . verifyPayload ( payload , currentTimestamp ) ;
281
270
await this . verifySignature ( rs256Token , isEmulator ) ;
282
271
283
272
return payload ;
@@ -295,9 +284,7 @@ export class FirebaseTokenVerifier {
295
284
currentTimestamp : number
296
285
) : RS256Token {
297
286
try {
298
- return isEmulator
299
- ? RS256Token . decode ( jwtToken , currentTimestamp )
300
- : RS256Token . decode ( jwtToken , currentTimestamp ) ;
287
+ return RS256Token . decode ( jwtToken , currentTimestamp , isEmulator )
301
288
} catch ( err ) {
302
289
const verifyJwtTokenDocsMessage =
303
290
` See ${ this . tokenInfo . url } ` +
@@ -316,7 +303,6 @@ export class FirebaseTokenVerifier {
316
303
317
304
private verifyPayload (
318
305
tokenPayload : DecodedPayload ,
319
- projectId : string ,
320
306
currentTimestamp : number
321
307
) : asserts tokenPayload is FirebaseIdToken {
322
308
const payload = tokenPayload ;
@@ -331,43 +317,43 @@ export class FirebaseTokenVerifier {
331
317
const createInvalidArgument = ( errorMessage : string ) =>
332
318
new FirebaseAuthError ( AuthClientErrorCode . INVALID_ARGUMENT , errorMessage ) ;
333
319
334
- if ( payload . aud !== projectId && payload . aud !== FIREBASE_AUDIENCE ) {
320
+ if ( payload . aud !== this . projectId && payload . aud !== FIREBASE_AUDIENCE ) {
335
321
throw createInvalidArgument (
336
322
`${ 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
340
326
) ;
341
327
}
342
328
343
- if ( payload . iss !== this . issuer + projectId ) {
329
+ if ( payload . iss !== this . issuer + this . projectId ) {
344
330
throw createInvalidArgument (
345
331
`${ 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
349
335
) ;
350
336
}
351
337
352
338
if ( payload . sub . length > 128 ) {
353
339
throw createInvalidArgument (
354
340
`${ this . tokenInfo . jwtName } has "sub" (subject) claim longer than 128 characters.` +
355
- verifyJwtTokenDocsMessage
341
+ verifyJwtTokenDocsMessage
356
342
) ;
357
343
}
358
344
359
345
// check auth_time claim
360
346
if ( typeof payload . auth_time !== "number" ) {
361
347
throw createInvalidArgument (
362
348
`${ this . tokenInfo . jwtName } has no "auth_time" claim. ` +
363
- verifyJwtTokenDocsMessage
349
+ verifyJwtTokenDocsMessage
364
350
) ;
365
351
}
366
352
367
353
if ( currentTimestamp < payload . auth_time ) {
368
354
throw createInvalidArgument (
369
355
`${ this . tokenInfo . jwtName } has incorrect "auth_time" claim. ` +
370
- verifyJwtTokenDocsMessage
356
+ verifyJwtTokenDocsMessage
371
357
) ;
372
358
}
373
359
}
@@ -376,9 +362,7 @@ export class FirebaseTokenVerifier {
376
362
token : RS256Token ,
377
363
isEmulator : boolean
378
364
) : Promise < void > {
379
- const verifier = isEmulator
380
- ? /* EMULATOR_VERIFIER */ this . signatureVerifier
381
- : this . signatureVerifier ;
365
+ const verifier = isEmulator ? EMULATOR_VERIFIER : this . signatureVerifier
382
366
return await verifier . verify ( token ) ;
383
367
}
384
368
@@ -466,18 +450,33 @@ export const ID_TOKEN_INFO: FirebaseTokenInfo = {
466
450
* Creates a new FirebaseTokenVerifier to verify Firebase ID tokens.
467
451
*
468
452
* @internal
469
- * @param app - Firebase app instance.
470
453
* @returns FirebaseTokenVerifier
471
454
*/
472
455
export function createIdTokenVerifier (
473
456
projectID : string ,
457
+ cacheKey : string ,
474
458
cfPublicKeyCacheNamespace : KVNamespace
475
459
) : FirebaseTokenVerifier {
476
- return new FirebaseTokenVerifier (
460
+ const signatureVerifier = PublicKeySignatureVerifier . withCertificateUrl (
477
461
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 ,
479
478
projectID ,
480
479
"https://securetoken.google.com/" ,
481
480
ID_TOKEN_INFO
482
481
) ;
483
- }
482
+ }
0 commit comments