@@ -19,7 +19,7 @@ import * as jwt from 'jsonwebtoken';
1919import { HttpClient , HttpRequestConfig , HttpError } from '../utils/api-request' ;
2020import { Agent } from 'http' ;
2121
22- const ALGORITHM_RS256 : jwt . Algorithm = 'RS256' as const ;
22+ export const ALGORITHM_RS256 : jwt . Algorithm = 'RS256' as const ;
2323
2424// `jsonwebtoken` converts errors from the `getKey` callback to its own `JsonWebTokenError` type
2525// and prefixes the error message with the following. Use the prefix to identify errors thrown
@@ -29,7 +29,7 @@ const JWT_CALLBACK_ERROR_PREFIX = 'error in secret or public key callback: ';
2929
3030const NO_MATCHING_KID_ERROR_MESSAGE = 'no-matching-kid-error' ;
3131
32- export type Dictionary = { [ key : string ] : any }
32+ export type Dictionary = { [ key : string ] : any }
3333
3434export type DecodedToken = {
3535 header : Dictionary ;
@@ -44,14 +44,17 @@ interface KeyFetcher {
4444 fetchPublicKeys ( ) : Promise < { [ key : string ] : string } > ;
4545}
4646
47- class UrlKeyFetcher implements KeyFetcher {
47+ /**
48+ * Class to fetch public keys from a client certificates URL.
49+ */
50+ export class UrlKeyFetcher implements KeyFetcher {
4851 private publicKeys : { [ key : string ] : string } ;
4952 private publicKeysExpireAt = 0 ;
5053
5154 constructor ( private clientCertUrl : string , private readonly httpAgent ?: Agent ) {
5255 if ( ! validator . isURL ( clientCertUrl ) ) {
5356 throw new Error (
54- 'The provided public client certificate URL is an invalid URL.' ,
57+ 'The provided public client certificate URL is not a valid URL.' ,
5558 ) ;
5659 }
5760 }
@@ -68,6 +71,11 @@ class UrlKeyFetcher implements KeyFetcher {
6871 return Promise . resolve ( this . publicKeys ) ;
6972 }
7073
74+ /**
75+ * Checks if the cached public keys need to be refreshed.
76+ *
77+ * @returns Whether the keys should be fetched from the client certs url or not.
78+ */
7179 private shouldRefresh ( ) : boolean {
7280 return ! this . publicKeys || this . publicKeysExpireAt <= Date . now ( ) ;
7381 }
@@ -120,7 +128,7 @@ class UrlKeyFetcher implements KeyFetcher {
120128}
121129
122130/**
123- * Verifies JWT signature with a public key.
131+ * Class for verifing JWT signature with a public key.
124132 */
125133export class PublicKeySignatureVerifier implements SignatureVerifier {
126134 constructor ( private keyFetcher : KeyFetcher ) {
@@ -134,10 +142,31 @@ export class PublicKeySignatureVerifier implements SignatureVerifier {
134142 }
135143
136144 public verify ( token : string ) : Promise < void > {
145+ if ( ! validator . isString ( token ) ) {
146+ return Promise . reject ( new JwtError ( JwtErrorCode . INVALID_ARGUMENT ,
147+ 'The provided token must be a string.' ) ) ;
148+ }
149+
137150 return verifyJwtSignature ( token , getKeyCallback ( this . keyFetcher ) , { algorithms : [ ALGORITHM_RS256 ] } ) ;
138151 }
139152}
140153
154+ /**
155+ * Class for verifing unsigned (emulator) JWTs.
156+ */
157+ export class EmulatorSignatureVerifier implements SignatureVerifier {
158+ public verify ( token : string ) : Promise < void > {
159+ // Signature checks skipped for emulator; no need to fetch public keys.
160+ return verifyJwtSignature ( token , '' ) ;
161+ }
162+ }
163+
164+ /**
165+ * Provides a callback to fetch public keys.
166+ *
167+ * @param fetcher KeyFetcher to fetch the keys from.
168+ * @returns A callback function that can be used to get keys in `jsonwebtoken`.
169+ */
141170function getKeyCallback ( fetcher : KeyFetcher ) : jwt . GetPublicKeyOrSecret {
142171 return ( header : jwt . JwtHeader , callback : jwt . SigningKeyCallback ) => {
143172 const kid = header . kid || '' ;
@@ -154,15 +183,22 @@ function getKeyCallback(fetcher: KeyFetcher): jwt.GetPublicKeyOrSecret {
154183 }
155184}
156185
157- export class EmulatorSignatureVerifier implements SignatureVerifier {
158- public verify ( token : string ) : Promise < void > {
159- // Signature checks skipped for emulator; no need to fetch public keys.
160- return verifyJwtSignature ( token , '' ) ;
186+ /**
187+ * Verifies the signature of a JWT using the provided secret or a function to fetch
188+ * the secret or public key.
189+ *
190+ * @param token The JWT to be verfied.
191+ * @param secretOrPublicKey The secret or a function to fetch the secret or public key.
192+ * @param options JWT verification options.
193+ * @returns A Promise resolving for a token with a valid signature.
194+ */
195+ export function verifyJwtSignature ( token : string , secretOrPublicKey : jwt . Secret | jwt . GetPublicKeyOrSecret ,
196+ options ?: jwt . VerifyOptions ) : Promise < void > {
197+ if ( ! validator . isString ( token ) ) {
198+ return Promise . reject ( new JwtError ( JwtErrorCode . INVALID_ARGUMENT ,
199+ 'The provided token must be a string.' ) ) ;
161200 }
162- }
163201
164- function verifyJwtSignature ( token : string , secretOrPublicKey : jwt . Secret | jwt . GetPublicKeyOrSecret ,
165- options ?: jwt . VerifyOptions ) : Promise < void > {
166202 return new Promise ( ( resolve , reject ) => {
167203 jwt . verify ( token , secretOrPublicKey , options ,
168204 ( error : jwt . VerifyErrors | null ) => {
@@ -176,12 +212,10 @@ function verifyJwtSignature(token: string, secretOrPublicKey: jwt.Secret | jwt.G
176212 } else if ( error . name === 'JsonWebTokenError' ) {
177213 if ( error . message && error . message . includes ( JWT_CALLBACK_ERROR_PREFIX ) ) {
178214 const message = error . message . split ( JWT_CALLBACK_ERROR_PREFIX ) . pop ( ) || 'Error fetching public keys.' ;
179- const code = ( message === NO_MATCHING_KID_ERROR_MESSAGE ) ? JwtErrorCode . KEY_FETCH_ERROR :
180- JwtErrorCode . INVALID_ARGUMENT ;
215+ const code = ( message === NO_MATCHING_KID_ERROR_MESSAGE ) ? JwtErrorCode . NO_MATCHING_KID :
216+ JwtErrorCode . KEY_FETCH_ERROR ;
181217 return reject ( new JwtError ( code , message ) ) ;
182218 }
183- return reject ( new JwtError ( JwtErrorCode . INVALID_SIGNATURE ,
184- 'The provided token has invalid signature.' ) ) ;
185219 }
186220 return reject ( new JwtError ( JwtErrorCode . INVALID_SIGNATURE , error . message ) ) ;
187221 } ) ;
@@ -190,6 +224,9 @@ function verifyJwtSignature(token: string, secretOrPublicKey: jwt.Secret | jwt.G
190224
191225/**
192226 * Decodes general purpose Firebase JWTs.
227+ *
228+ * @param jwtToken JWT token to be decoded.
229+ * @returns Decoded token containing the header and payload.
193230 */
194231export function decodeJwt ( jwtToken : string ) : Promise < DecodedToken > {
195232 if ( ! validator . isString ( jwtToken ) ) {
@@ -233,5 +270,6 @@ export enum JwtErrorCode {
233270 INVALID_CREDENTIAL = 'invalid-credential' ,
234271 TOKEN_EXPIRED = 'token-expired' ,
235272 INVALID_SIGNATURE = 'invalid-token' ,
236- KEY_FETCH_ERROR = 'no-matching-kid-error' ,
273+ NO_MATCHING_KID = 'no-matching-kid-error' ,
274+ KEY_FETCH_ERROR = 'key-fetch-error' ,
237275}
0 commit comments