From 44956140f3655796baf694de999a46d816137b29 Mon Sep 17 00:00:00 2001 From: Andrei Tatar Date: Thu, 10 Mar 2022 20:28:27 +0200 Subject: [PATCH] don't retry auth only in 2 very specific cases #165 - 400 response - bad auth config --- src/index.ts | 9 ----- src/nora/connection.ts | 74 ++++++++++++++++++------------------------ 2 files changed, 32 insertions(+), 51 deletions(-) diff --git a/src/index.ts b/src/index.ts index 5f32c99..2162d4f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -127,15 +127,6 @@ export function singleton(): MonoTypeOperatorFunction { ); } -export function shouldRetryRequest(response: NodeFetchResponse) { - if (response.status === 429) { - return true; - } - - const status = Math.floor(response.status / 100); - return status !== 2 && status !== 4; -} - export class HttpError extends Error { constructor( public readonly statusCode: number, diff --git a/src/nora/connection.ts b/src/nora/connection.ts index cf2d44f..8f62209 100644 --- a/src/nora/connection.ts +++ b/src/nora/connection.ts @@ -1,10 +1,10 @@ import { deleteApp, FirebaseApp, initializeApp } from 'firebase/app'; import { getAuth, signInWithEmailAndPassword, signInWithCustomToken, UserCredential } from 'firebase/auth'; -import { merge, Observable, of, timer } from 'rxjs'; +import { firstValueFrom, merge, NEVER, Observable, of, timer } from 'rxjs'; import { delayWhen, finalize, ignoreElements, map, retryWhen, switchMap, tap } from 'rxjs/operators'; import fetch from 'node-fetch'; -import { getHash, HttpError, Logger, publishReplayRefCountWithDelay, shouldRetryRequest } from '..'; +import { getHash, HttpError, Logger, publishReplayRefCountWithDelay } from '..'; import { API_ENDPOINT, FIREBASE_CONFIG, NoraConfig, USER_AGENT } from '../config'; import { AsyncCommandsRegistry } from './async-commands.registry'; import { DeviceContext } from './device-context'; @@ -79,51 +79,41 @@ export class FirebaseConnection { private static async authenticate(app: FirebaseApp, config: NoraConfig): Promise { const auth = getAuth(app); - try { - if (config.password?.length) { - return await signInWithEmailAndPassword(auth, config.email, config.password); - } else if (config.sso?.length) { - const customToken = await this.exchangeToken(config.sso); - return await signInWithCustomToken(auth, customToken); - } else { - throw new Error('nora: invalid auth config'); - } - } catch (err) { - this.logger?.error(`nora: ${err}`); - await new Promise(() => { - // never resolve, there's nothing to retry - }); - throw new Error(); // make TS happy :) + if (config.password?.length) { + return await signInWithEmailAndPassword(auth, config.email, config.password); + } else if (config.sso?.length) { + const customToken = await this.exchangeToken(config.sso); + return await signInWithCustomToken(auth, customToken); + } else { + this.logger?.error('nora: invalid auth config; not retrying'); + return firstValueFrom(NEVER); } } - private static async exchangeToken(ssoToken: string, tries = 3): Promise { - while (tries--) { - const url = `${API_ENDPOINT}/sso/exchange`; - const response = await fetch(url, { - method: 'POST', - headers: { - 'content-type': 'application/json', - 'user-agent': USER_AGENT, - }, - body: JSON.stringify({ - token: ssoToken - }), - }); - if (response.status !== 200) { - const shouldRetry = shouldRetryRequest(response); - if (!shouldRetry || !tries) { - throw new HttpError(response.status, await response.text()); - } - const delay = Math.round(Math.random() * 20) * 50 + 300; - await new Promise(resolve => setTimeout(resolve, delay)); - continue; - } - const json = await response.json(); - return json.token; + private static async exchangeToken(ssoToken: string): Promise { + const url = `${API_ENDPOINT}/sso/exchange`; + const response = await fetch(url, { + method: 'POST', + headers: { + 'content-type': 'application/json', + 'user-agent': USER_AGENT, + }, + body: JSON.stringify({ + token: ssoToken + }), + }); + + if (response.status === 200) { + const { token } = await response.json(); + return token; + } + + if (response.status === 400) { + this.logger?.error(`nora: invalid sso token; not retrying - ${await response.text()}`); + return firstValueFrom(NEVER); } - throw new Error('could not exchange sso token'); + throw new HttpError(response.status, await response.text()); } private static createFirebaseApp() {