From e46a324d9fbb5e69dd2a5ad0fc816af9e0c535a3 Mon Sep 17 00:00:00 2001 From: Tyler <18113850+dshukertjr@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:32:00 +0900 Subject: [PATCH] fix: add password recoery flow support for pkce (#813) ## What kind of change does this PR introduce? Currently, `PASSWORD_RECOVERY` auth event is not emitted during a password recovery flow using pkce. ## What is the current behavior? Calling `resetPasswordForEmail()` method ## What is the new behavior? Calling `resetPasswordForEmail()` sets a code verifier and the auth flow `PASSWORD_RECOVERY` with a `/` separator on the local storage like this: `${codeVerifier}/PASSWORD_RECOVERY` Within `_exchangeCodeForSession()`, the auth flow and the code verifier is extracted from local storage, and if the auth flow is `PASSWORD_RECOVERY`, then `PASSWORD_RECOVERY` event is fired instead of `SIGNED_IN` event. --- src/GoTrueClient.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index 6337df87f..bd45d36f7 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -499,8 +499,16 @@ export default class GoTrueClient { }) } - private async _exchangeCodeForSession(authCode: string): Promise { - const codeVerifier = await getItemAsync(this.storage, `${this.storageKey}-code-verifier`) + private async _exchangeCodeForSession(authCode: string): Promise< + | { + data: { session: Session; user: User; redirectType: string | null } + error: null + } + | { data: { session: null; user: null; redirectType: null }; error: AuthError } + > { + const [codeVerifier, redirectType] = ( + (await getItemAsync(this.storage, `${this.storageKey}-code-verifier`)) as string + ).split('/') const { data, error } = await _request( this.fetch, 'POST', @@ -516,15 +524,18 @@ export default class GoTrueClient { ) await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) if (error) { - return { data: { user: null, session: null }, error } + return { data: { user: null, session: null, redirectType: null }, error } } else if (!data || !data.session || !data.user) { - return { data: { user: null, session: null }, error: new AuthInvalidTokenResponseError() } + return { + data: { user: null, session: null, redirectType: null }, + error: new AuthInvalidTokenResponseError(), + } } if (data.session) { await this._saveSession(data.session) await this._notifyAllSubscribers('SIGNED_IN', data.session) } - return { data, error } + return { data: { ...data, redirectType: redirectType ?? null }, error } } /** @@ -1538,7 +1549,11 @@ export default class GoTrueClient { let codeChallengeMethod: string | null = null if (this.flowType === 'pkce') { const codeVerifier = generatePKCEVerifier() - await setItemAsync(this.storage, `${this.storageKey}-code-verifier`, codeVerifier) + await setItemAsync( + this.storage, + `${this.storageKey}-code-verifier`, + `${codeVerifier}/PASSWORD_RECOVERY` + ) codeChallenge = await generatePKCEChallenge(codeVerifier) codeChallengeMethod = codeVerifier === codeChallenge ? 'plain' : 's256' }