From 2683e50e2910d2a6113e2a95e06164471be5d1c0 Mon Sep 17 00:00:00 2001 From: HiraiKyo Date: Wed, 12 Jun 2024 13:17:17 +0900 Subject: [PATCH 1/3] fixed iat error of 1 seconds diff --- src/token-verifier.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/token-verifier.ts b/src/token-verifier.ts index 87ee96a..1aece12 100644 --- a/src/token-verifier.ts +++ b/src/token-verifier.ts @@ -242,7 +242,7 @@ export class FirebaseTokenVerifier { } private async decodeAndVerify(token: string, isEmulator: boolean): Promise { - const currentTimestamp = Math.floor(Date.now() / 1000); + const currentTimestamp = Math.ceil(Date.now() / 1000); try { const rs256Token = this.safeDecode(token, isEmulator, currentTimestamp); const { payload } = rs256Token.decodedToken; From 255d813c5fa246d2dcbc118b18d5b97b2f181183 Mon Sep 17 00:00:00 2001 From: HiraiKyo Date: Sat, 6 Jul 2024 15:03:19 +0900 Subject: [PATCH 2/3] Add clockSkewSeconds to tolerate iat error --- src/auth.ts | 7 ++++--- src/token-verifier.ts | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/auth.ts b/src/auth.ts index a191722..6fb94d5 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -54,14 +54,15 @@ export class BaseAuth { * @param env - An optional parameter specifying the environment in which the function is running. * If the function is running in an emulator environment, this should be set to `EmulatorEnv`. * If not specified, the function will assume it is running in a production environment. - * + * @param clockSkewSeconds - The number of seconds to tolerate when checking the `iat`. + * This is to deal with small clock differences among different servers. * @returns A promise fulfilled with the * token's decoded claims if the ID token is valid; otherwise, a rejected * promise. */ - public async verifyIdToken(idToken: string, checkRevoked = false, env?: EmulatorEnv): Promise { + public async verifyIdToken(idToken: string, checkRevoked = false, env?: EmulatorEnv, clockSkewSeconds?: number): Promise { const isEmulator = useEmulator(env); - const decodedIdToken = await this.idTokenVerifier.verifyJWT(idToken, isEmulator); + const decodedIdToken = await this.idTokenVerifier.verifyJWT(idToken, isEmulator, clockSkewSeconds); // Whether to check if the token was revoked. if (checkRevoked) { return await this.verifyDecodedJWTNotRevokedOrDisabled(decodedIdToken, AuthClientErrorCode.ID_TOKEN_REVOKED, env); diff --git a/src/token-verifier.ts b/src/token-verifier.ts index 1aece12..c99a373 100644 --- a/src/token-verifier.ts +++ b/src/token-verifier.ts @@ -226,23 +226,31 @@ export class FirebaseTokenVerifier { * * @param jwtToken - The Firebase Auth JWT token to verify. * @param isEmulator - Whether to accept Auth Emulator tokens. + * @param clockSkewSeconds - The number of seconds to tolerate when checking the token's iat. Must be between 0-60, and an integer. Defualts to 0. * @returns A promise fulfilled with the decoded claims of the Firebase Auth ID token. */ - public verifyJWT(jwtToken: string, isEmulator = false): Promise { + public verifyJWT(jwtToken: string, isEmulator = false, clockSkewSeconds: number = 0): Promise { if (!isString(jwtToken)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, `First argument to ${this.tokenInfo.verifyApiName} must be a ${this.tokenInfo.jwtName} string.` ); } - return this.decodeAndVerify(jwtToken, isEmulator).then(payload => { + + if (clockSkewSeconds < 0 || clockSkewSeconds > 60 || !Number.isInteger(clockSkewSeconds)) { + throw new FirebaseAuthError( + AuthClientErrorCode.INVALID_ARGUMENT, + 'clockSkewSeconds must be an integer between 0 and 60.' + ) + } + return this.decodeAndVerify(jwtToken, isEmulator, 0).then(payload => { payload.uid = payload.sub; return payload; }); } - private async decodeAndVerify(token: string, isEmulator: boolean): Promise { - const currentTimestamp = Math.ceil(Date.now() / 1000); + private async decodeAndVerify(token: string, isEmulator: boolean, clockSkewSeconds: number = 0): Promise { + const currentTimestamp = Math.floor(Date.now() / 1000) + clockSkewSeconds; try { const rs256Token = this.safeDecode(token, isEmulator, currentTimestamp); const { payload } = rs256Token.decodedToken; From a9f993f1188aa8a6b9c9053845ff6235cef3c58d Mon Sep 17 00:00:00 2001 From: HiraiKyo Date: Thu, 18 Jul 2024 15:39:54 +0900 Subject: [PATCH 3/3] Fixed arg for clockSkew and default to 5. --- src/token-verifier.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/token-verifier.ts b/src/token-verifier.ts index c99a373..83e0ee6 100644 --- a/src/token-verifier.ts +++ b/src/token-verifier.ts @@ -229,7 +229,7 @@ export class FirebaseTokenVerifier { * @param clockSkewSeconds - The number of seconds to tolerate when checking the token's iat. Must be between 0-60, and an integer. Defualts to 0. * @returns A promise fulfilled with the decoded claims of the Firebase Auth ID token. */ - public verifyJWT(jwtToken: string, isEmulator = false, clockSkewSeconds: number = 0): Promise { + public verifyJWT(jwtToken: string, isEmulator = false, clockSkewSeconds: number = 5): Promise { if (!isString(jwtToken)) { throw new FirebaseAuthError( AuthClientErrorCode.INVALID_ARGUMENT, @@ -243,13 +243,13 @@ export class FirebaseTokenVerifier { 'clockSkewSeconds must be an integer between 0 and 60.' ) } - return this.decodeAndVerify(jwtToken, isEmulator, 0).then(payload => { + return this.decodeAndVerify(jwtToken, isEmulator, clockSkewSeconds).then(payload => { payload.uid = payload.sub; return payload; }); } - private async decodeAndVerify(token: string, isEmulator: boolean, clockSkewSeconds: number = 0): Promise { + private async decodeAndVerify(token: string, isEmulator: boolean, clockSkewSeconds: number = 5): Promise { const currentTimestamp = Math.floor(Date.now() / 1000) + clockSkewSeconds; try { const rs256Token = this.safeDecode(token, isEmulator, currentTimestamp);