diff --git a/packages/waas/src/auth.ts b/packages/waas/src/auth.ts index ccb49c607..b3a8dc7a8 100644 --- a/packages/waas/src/auth.ts +++ b/packages/waas/src/auth.ts @@ -170,7 +170,9 @@ export class SequenceWaaS { private validationRequiredCallback: (() => void)[] = [] private emailConflictCallback: ((info: EmailConflictInfo, forceCreate: () => Promise) => Promise)[] = [] private emailAuthCodeRequiredCallback: ((respondWithCode: (code: string) => Promise) => Promise)[] = [] + private confirmationRequiredCallback: ((respondWithCode: (code: string) => Promise) => Promise)[] = [] private validationRequiredSalt: string + private lastConfirmationAttemptAt: Date | undefined public readonly config: Required & Required & ExtendedSequenceConfig @@ -315,6 +317,13 @@ export class SequenceWaaS { } } + onConfirmationRequired(callback: (respondWithCode: (code: string) => Promise) => Promise) { + this.confirmationRequiredCallback.push(callback) + return () => { + this.confirmationRequiredCallback = this.confirmationRequiredCallback.filter(c => c !== callback) + } + } + private async handleValidationRequired({ onValidationRequired }: ValidationArgs = {}): Promise { const proceed = onValidationRequired ? onValidationRequired() : true if (!proceed) { @@ -336,8 +345,42 @@ export class SequenceWaaS { } private async handleConfirmationRequired(response: ConfirmationRequiredResponse) { - const intent2 = await this.waas.confirmIntent(response.data.salt, "111111") - return intent2 + if (this.confirmationRequiredCallback.length === 0) { + throw new Error('Missing confirmationRequired callback') + } + + return new Promise((resolve, reject) => { + const respondToChallenge = async (answer: string) => { + if (this.lastConfirmationAttemptAt) { + const timeSinceLastAttempt = new Date().getTime() - this.lastConfirmationAttemptAt.getTime() + if (timeSinceLastAttempt < 32000) { + console.info(`Waiting ${Math.ceil((32000 - timeSinceLastAttempt) / 1000)}s before retrying confirmation attempt`) + await new Promise(resolve => setTimeout(resolve, 32000 - timeSinceLastAttempt)) + } + } + + this.lastConfirmationAttemptAt = new Date() + + const intent = await this.waas.confirmIntent(response.data.salt, answer) + + try { + const response2 = await this.sendIntent(intent) + resolve(response2) + } catch (e) { + if (e instanceof AnswerIncorrectError) { + // This will NOT resolve NOR reject the top-level promise returned from signIn, it'll keep being pending + // It allows the caller to retry calling the respondToChallenge callback + throw e + } else { + reject(e) + } + } + } + + for (const callback of this.confirmationRequiredCallback) { + callback(respondToChallenge) + } + }) } private headers() { @@ -783,12 +826,11 @@ export class SequenceWaaS { } if (isConfirmationRequiredResponse(response)) { - const intent2 = await this.handleConfirmationRequired(response) - if (intent2) { - const response2 = await this.sendIntent(intent2) - if (isExpectedResponse(response2)) { - return response2 - } + const response2 = await this.handleConfirmationRequired(response) + if (isExpectedResponse(response2)) { + return response2 + } else { + throw new Error(JSON.stringify(response2)) } }