1717import { AuthClientErrorCode , FirebaseAuthError , ErrorInfo } from '../utils/error' ;
1818import * as util from '../utils/index' ;
1919import * as validator from '../utils/validator' ;
20- import * as jwt from 'jsonwebtoken' ;
2120import {
2221 DecodedToken , decodeJwt , JwtError , JwtErrorCode ,
23- EmulatorSignatureVerifier , PublicKeySignatureVerifier , UrlKeyFetcher ,
22+ EmulatorSignatureVerifier , PublicKeySignatureVerifier ,
2423} from '../utils/jwt' ;
2524import { FirebaseApp } from '../firebase-app' ;
2625import { auth } from './index' ;
@@ -39,6 +38,8 @@ const CLIENT_CERT_URL = 'https://www.googleapis.com/robot/v1/metadata/x509/secur
3938// URL containing the public keys for Firebase session cookies. This will be updated to a different URL soon.
4039const SESSION_COOKIE_CERT_URL = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys' ;
4140
41+ const EMULATOR_VERIFIER = new EmulatorSignatureVerifier ( ) ;
42+
4243/** User facing token information related to the Firebase ID token. */
4344export const ID_TOKEN_INFO : FirebaseTokenInfo = {
4445 url : 'https://firebase.google.com/docs/auth/admin/verify-id-tokens' ,
@@ -78,20 +79,14 @@ export class FirebaseTokenVerifier {
7879 private readonly shortNameArticle : string ;
7980 private readonly signatureVerifier : PublicKeySignatureVerifier ;
8081
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 ,
8383 private readonly app : FirebaseApp ) {
8484
8585 if ( ! validator . isURL ( clientCertUrl ) ) {
8686 throw new FirebaseAuthError (
8787 AuthClientErrorCode . INVALID_ARGUMENT ,
8888 'The provided public client certificate URL is an invalid URL.' ,
8989 ) ;
90- } else if ( ! validator . isNonEmptyString ( algorithm ) ) {
91- throw new FirebaseAuthError (
92- AuthClientErrorCode . INVALID_ARGUMENT ,
93- 'The provided JWT algorithm is an empty string.' ,
94- ) ;
9590 } else if ( ! validator . isURL ( issuer ) ) {
9691 throw new FirebaseAuthError (
9792 AuthClientErrorCode . INVALID_ARGUMENT ,
@@ -130,19 +125,18 @@ export class FirebaseTokenVerifier {
130125 }
131126 this . shortNameArticle = tokenInfo . shortName . charAt ( 0 ) . match ( / [ a e i o u ] / i) ? 'an' : 'a' ;
132127
133- this . signatureVerifier = new PublicKeySignatureVerifier (
134- new UrlKeyFetcher ( clientCertUrl , app . options . httpAgent ) ) ;
128+ this . signatureVerifier =
129+ PublicKeySignatureVerifier . withCertificateUrl ( clientCertUrl , app . options . httpAgent ) ;
135130
136131 // For backward compatibility, the project ID is validated in the verification call.
137132 }
138133
139134 /**
140135 * Verifies the format and signature of a Firebase Auth JWT token.
141136 *
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.
146140 */
147141 public verifyJWT ( jwtToken : string , isEmulator = false ) : Promise < DecodedIdToken > {
148142 if ( ! validator . isString ( jwtToken ) ) {
@@ -178,26 +172,17 @@ export class FirebaseTokenVerifier {
178172 }
179173
180174 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 ) ;
183178 return this . verifySignature ( token , isEmulator )
184- . then ( ( ) => decoded ) ;
179+ . then ( ( ) => decodedToken ) ;
185180 } ) ;
186181 }
187182
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-
195183 private safeDecode ( jwtToken : string ) : Promise < DecodedToken > {
196184 return decodeJwt ( jwtToken )
197- . catch ( ( err ) => {
198- if ( ! ( err instanceof JwtError ) ) {
199- throw err ;
200- }
185+ . catch ( ( err : JwtError ) => {
201186 if ( err . code == JwtErrorCode . INVALID_ARGUMENT ) {
202187 const verifyJwtTokenDocsMessage = ` See ${ this . tokenInfo . url } ` +
203188 `for details on how to retrieve ${ this . shortNameArticle } ${ this . tokenInfo . shortName } .` ;
@@ -211,7 +196,7 @@ export class FirebaseTokenVerifier {
211196 } ) ;
212197 }
213198
214- private validateTokenContent (
199+ private verifyContent (
215200 fullDecodedToken : DecodedToken ,
216201 projectId : string | null ,
217202 isEmulator : boolean ) : void {
@@ -240,8 +225,8 @@ export class FirebaseTokenVerifier {
240225 }
241226
242227 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 ' +
245230 '"' + header . alg + '".' + verifyJwtTokenDocsMessage ;
246231 } else if ( payload . aud !== projectId ) {
247232 errorMessage = `${ this . tokenInfo . jwtName } has incorrect "aud" (audience) claim. Expected "` +
@@ -266,7 +251,7 @@ export class FirebaseTokenVerifier {
266251
267252 private verifySignature ( jwtToken : string , isEmulator : boolean ) :
268253 Promise < void > {
269- const verifier = isEmulator ? new EmulatorSignatureVerifier ( ) : this . signatureVerifier ;
254+ const verifier = isEmulator ? EMULATOR_VERIFIER : this . signatureVerifier ;
270255 return verifier . verify ( jwtToken )
271256 . catch ( ( error ) => {
272257 throw this . mapJwtErrorToAuthError ( error ) ;
@@ -285,11 +270,11 @@ export class FirebaseTokenVerifier {
285270 verifyJwtTokenDocsMessage ;
286271 return new FirebaseAuthError ( this . tokenInfo . expiredErrorCode , errorMessage ) ;
287272 }
288- else if ( error . code === JwtErrorCode . INVALID_TOKEN ) {
273+ else if ( error . code === JwtErrorCode . INVALID_SIGNATURE ) {
289274 const errorMessage = `${ this . tokenInfo . jwtName } has invalid signature.` + verifyJwtTokenDocsMessage ;
290275 return new FirebaseAuthError ( AuthClientErrorCode . INVALID_ARGUMENT , errorMessage ) ;
291276 }
292- else if ( error . code === JwtErrorCode . NO_MATCHING_KID ) {
277+ else if ( error . code === JwtErrorCode . KEY_FETCH_ERROR ) {
293278 const errorMessage = `${ this . tokenInfo . jwtName } has "kid" claim which does not ` +
294279 `correspond to a known public key. Most likely the ${ this . tokenInfo . shortName } ` +
295280 'is expired, so get a fresh token from your client app and try again.' ;
@@ -302,13 +287,12 @@ export class FirebaseTokenVerifier {
302287/**
303288 * Creates a new FirebaseTokenVerifier to verify Firebase ID tokens.
304289 *
305- * @param { FirebaseApp } app Firebase app instance.
306- * @return { FirebaseTokenVerifier }
290+ * @param app Firebase app instance.
291+ * @return FirebaseTokenVerifier
307292 */
308293export function createIdTokenVerifier ( app : FirebaseApp ) : FirebaseTokenVerifier {
309294 return new FirebaseTokenVerifier (
310295 CLIENT_CERT_URL ,
311- ALGORITHM_RS256 ,
312296 'https://securetoken.google.com/' ,
313297 ID_TOKEN_INFO ,
314298 app
@@ -318,13 +302,12 @@ export function createIdTokenVerifier(app: FirebaseApp): FirebaseTokenVerifier {
318302/**
319303 * Creates a new FirebaseTokenVerifier to verify Firebase session cookies.
320304 *
321- * @param { FirebaseApp } app Firebase app instance.
322- * @return { FirebaseTokenVerifier }
305+ * @param app Firebase app instance.
306+ * @return FirebaseTokenVerifier
323307 */
324308export function createSessionCookieVerifier ( app : FirebaseApp ) : FirebaseTokenVerifier {
325309 return new FirebaseTokenVerifier (
326310 SESSION_COOKIE_CERT_URL ,
327- ALGORITHM_RS256 ,
328311 'https://session.firebase.google.com/' ,
329312 SESSION_COOKIE_INFO ,
330313 app
0 commit comments