17
17
import { AuthClientErrorCode , FirebaseAuthError , ErrorInfo } from '../utils/error' ;
18
18
import * as util from '../utils/index' ;
19
19
import * as validator from '../utils/validator' ;
20
- import * as jwt from 'jsonwebtoken' ;
21
20
import {
22
21
DecodedToken , decodeJwt , JwtError , JwtErrorCode ,
23
- EmulatorSignatureVerifier , PublicKeySignatureVerifier , UrlKeyFetcher ,
22
+ EmulatorSignatureVerifier , PublicKeySignatureVerifier ,
24
23
} from '../utils/jwt' ;
25
24
import { FirebaseApp } from '../firebase-app' ;
26
25
import { auth } from './index' ;
@@ -39,6 +38,8 @@ const CLIENT_CERT_URL = 'https://www.googleapis.com/robot/v1/metadata/x509/secur
39
38
// URL containing the public keys for Firebase session cookies. This will be updated to a different URL soon.
40
39
const SESSION_COOKIE_CERT_URL = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys' ;
41
40
41
+ const EMULATOR_VERIFIER = new EmulatorSignatureVerifier ( ) ;
42
+
42
43
/** User facing token information related to the Firebase ID token. */
43
44
export const ID_TOKEN_INFO : FirebaseTokenInfo = {
44
45
url : 'https://firebase.google.com/docs/auth/admin/verify-id-tokens' ,
@@ -78,20 +79,14 @@ export class FirebaseTokenVerifier {
78
79
private readonly shortNameArticle : string ;
79
80
private readonly signatureVerifier : PublicKeySignatureVerifier ;
80
81
81
- constructor ( clientCertUrl : string , private algorithm : jwt . Algorithm ,
82
- private issuer : string , private tokenInfo : FirebaseTokenInfo ,
82
+ constructor ( clientCertUrl : string , private issuer : string , private tokenInfo : FirebaseTokenInfo ,
83
83
private readonly app : FirebaseApp ) {
84
84
85
85
if ( ! validator . isURL ( clientCertUrl ) ) {
86
86
throw new FirebaseAuthError (
87
87
AuthClientErrorCode . INVALID_ARGUMENT ,
88
88
'The provided public client certificate URL is an invalid URL.' ,
89
89
) ;
90
- } else if ( ! validator . isNonEmptyString ( algorithm ) ) {
91
- throw new FirebaseAuthError (
92
- AuthClientErrorCode . INVALID_ARGUMENT ,
93
- 'The provided JWT algorithm is an empty string.' ,
94
- ) ;
95
90
} else if ( ! validator . isURL ( issuer ) ) {
96
91
throw new FirebaseAuthError (
97
92
AuthClientErrorCode . INVALID_ARGUMENT ,
@@ -130,19 +125,18 @@ export class FirebaseTokenVerifier {
130
125
}
131
126
this . shortNameArticle = tokenInfo . shortName . charAt ( 0 ) . match ( / [ a e i o u ] / i) ? 'an' : 'a' ;
132
127
133
- this . signatureVerifier = new PublicKeySignatureVerifier (
134
- new UrlKeyFetcher ( clientCertUrl , app . options . httpAgent ) ) ;
128
+ this . signatureVerifier =
129
+ PublicKeySignatureVerifier . withCertificateUrl ( clientCertUrl , app . options . httpAgent ) ;
135
130
136
131
// For backward compatibility, the project ID is validated in the verification call.
137
132
}
138
133
139
134
/**
140
135
* Verifies the format and signature of a Firebase Auth JWT token.
141
136
*
142
- * @param {string } jwtToken The Firebase Auth JWT token to verify.
143
- * @param {boolean= } isEmulator Whether to accept Auth Emulator tokens.
144
- * @return {Promise<DecodedIdToken> } A promise fulfilled with the decoded claims of the Firebase Auth ID
145
- * token.
137
+ * @param jwtToken The Firebase Auth JWT token to verify.
138
+ * @param isEmulator Whether to accept Auth Emulator tokens.
139
+ * @return A promise fulfilled with the decoded claims of the Firebase Auth ID token.
146
140
*/
147
141
public verifyJWT ( jwtToken : string , isEmulator = false ) : Promise < DecodedIdToken > {
148
142
if ( ! validator . isString ( jwtToken ) ) {
@@ -178,26 +172,17 @@ export class FirebaseTokenVerifier {
178
172
}
179
173
180
174
private decodeAndVerify ( token : string , projectId : string , isEmulator : boolean ) : Promise < DecodedToken > {
181
- return this . verifyContent ( token , projectId , isEmulator )
182
- . then ( ( decoded ) => {
175
+ return this . safeDecode ( token )
176
+ . then ( ( decodedToken ) => {
177
+ this . verifyContent ( decodedToken , projectId , isEmulator ) ;
183
178
return this . verifySignature ( token , isEmulator )
184
- . then ( ( ) => decoded ) ;
179
+ . then ( ( ) => decodedToken ) ;
185
180
} ) ;
186
181
}
187
182
188
- private verifyContent ( token : string , projectId : string , isEmulator : boolean ) : Promise < DecodedToken > {
189
- return this . safeDecode ( token ) . then ( ( decodedToken ) => {
190
- this . validateTokenContent ( decodedToken , projectId , isEmulator ) ;
191
- return Promise . resolve ( decodedToken ) ;
192
- } ) ;
193
- }
194
-
195
183
private safeDecode ( jwtToken : string ) : Promise < DecodedToken > {
196
184
return decodeJwt ( jwtToken )
197
- . catch ( ( err ) => {
198
- if ( ! ( err instanceof JwtError ) ) {
199
- throw err ;
200
- }
185
+ . catch ( ( err : JwtError ) => {
201
186
if ( err . code == JwtErrorCode . INVALID_ARGUMENT ) {
202
187
const verifyJwtTokenDocsMessage = ` See ${ this . tokenInfo . url } ` +
203
188
`for details on how to retrieve ${ this . shortNameArticle } ${ this . tokenInfo . shortName } .` ;
@@ -211,7 +196,7 @@ export class FirebaseTokenVerifier {
211
196
} ) ;
212
197
}
213
198
214
- private validateTokenContent (
199
+ private verifyContent (
215
200
fullDecodedToken : DecodedToken ,
216
201
projectId : string | null ,
217
202
isEmulator : boolean ) : void {
@@ -240,8 +225,8 @@ export class FirebaseTokenVerifier {
240
225
}
241
226
242
227
errorMessage += verifyJwtTokenDocsMessage ;
243
- } else if ( ! isEmulator && header . alg !== this . algorithm ) {
244
- errorMessage = `${ this . tokenInfo . jwtName } has incorrect algorithm. Expected "` + this . algorithm + '" but got ' +
228
+ } else if ( ! isEmulator && header . alg !== ALGORITHM_RS256 ) {
229
+ errorMessage = `${ this . tokenInfo . jwtName } has incorrect algorithm. Expected "` + ALGORITHM_RS256 + '" but got ' +
245
230
'"' + header . alg + '".' + verifyJwtTokenDocsMessage ;
246
231
} else if ( payload . aud !== projectId ) {
247
232
errorMessage = `${ this . tokenInfo . jwtName } has incorrect "aud" (audience) claim. Expected "` +
@@ -266,7 +251,7 @@ export class FirebaseTokenVerifier {
266
251
267
252
private verifySignature ( jwtToken : string , isEmulator : boolean ) :
268
253
Promise < void > {
269
- const verifier = isEmulator ? new EmulatorSignatureVerifier ( ) : this . signatureVerifier ;
254
+ const verifier = isEmulator ? EMULATOR_VERIFIER : this . signatureVerifier ;
270
255
return verifier . verify ( jwtToken )
271
256
. catch ( ( error ) => {
272
257
throw this . mapJwtErrorToAuthError ( error ) ;
@@ -285,11 +270,11 @@ export class FirebaseTokenVerifier {
285
270
verifyJwtTokenDocsMessage ;
286
271
return new FirebaseAuthError ( this . tokenInfo . expiredErrorCode , errorMessage ) ;
287
272
}
288
- else if ( error . code === JwtErrorCode . INVALID_TOKEN ) {
273
+ else if ( error . code === JwtErrorCode . INVALID_SIGNATURE ) {
289
274
const errorMessage = `${ this . tokenInfo . jwtName } has invalid signature.` + verifyJwtTokenDocsMessage ;
290
275
return new FirebaseAuthError ( AuthClientErrorCode . INVALID_ARGUMENT , errorMessage ) ;
291
276
}
292
- else if ( error . code === JwtErrorCode . NO_MATCHING_KID ) {
277
+ else if ( error . code === JwtErrorCode . KEY_FETCH_ERROR ) {
293
278
const errorMessage = `${ this . tokenInfo . jwtName } has "kid" claim which does not ` +
294
279
`correspond to a known public key. Most likely the ${ this . tokenInfo . shortName } ` +
295
280
'is expired, so get a fresh token from your client app and try again.' ;
@@ -302,13 +287,12 @@ export class FirebaseTokenVerifier {
302
287
/**
303
288
* Creates a new FirebaseTokenVerifier to verify Firebase ID tokens.
304
289
*
305
- * @param { FirebaseApp } app Firebase app instance.
306
- * @return { FirebaseTokenVerifier }
290
+ * @param app Firebase app instance.
291
+ * @return FirebaseTokenVerifier
307
292
*/
308
293
export function createIdTokenVerifier ( app : FirebaseApp ) : FirebaseTokenVerifier {
309
294
return new FirebaseTokenVerifier (
310
295
CLIENT_CERT_URL ,
311
- ALGORITHM_RS256 ,
312
296
'https://securetoken.google.com/' ,
313
297
ID_TOKEN_INFO ,
314
298
app
@@ -318,13 +302,12 @@ export function createIdTokenVerifier(app: FirebaseApp): FirebaseTokenVerifier {
318
302
/**
319
303
* Creates a new FirebaseTokenVerifier to verify Firebase session cookies.
320
304
*
321
- * @param { FirebaseApp } app Firebase app instance.
322
- * @return { FirebaseTokenVerifier }
305
+ * @param app Firebase app instance.
306
+ * @return FirebaseTokenVerifier
323
307
*/
324
308
export function createSessionCookieVerifier ( app : FirebaseApp ) : FirebaseTokenVerifier {
325
309
return new FirebaseTokenVerifier (
326
310
SESSION_COOKIE_CERT_URL ,
327
- ALGORITHM_RS256 ,
328
311
'https://session.firebase.google.com/' ,
329
312
SESSION_COOKIE_INFO ,
330
313
app
0 commit comments