From a43c63232011baf99f63c4c4e761a0993c59f33b Mon Sep 17 00:00:00 2001 From: Jason Creviston Date: Mon, 2 Dec 2024 12:50:18 -0500 Subject: [PATCH 1/3] fix: code verifier remains in storage during edge cases --- src/GoTrueClient.ts | 24 +++++++++++++++++++++++- src/lib/types.ts | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index 0e0c880d..bcf2e677 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -460,6 +460,8 @@ export default class GoTrueClient { const { data, error } = res if (error || !data) { + await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) + await this._notifyAllSubscribers('STORAGE_UPDATED', null) return { data: { user: null, session: null }, error: error } } @@ -473,6 +475,8 @@ export default class GoTrueClient { return { data: { user, session }, error: null } } catch (error) { + await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) + await this._notifyAllSubscribers('STORAGE_UPDATED', null) if (isAuthError(error)) { return { data: { user: null, session: null }, error } } @@ -613,6 +617,8 @@ export default class GoTrueClient { return { data: { ...data, redirectType: redirectType ?? null }, error } } catch (error) { if (isAuthError(error)) { + await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) + await this._notifyAllSubscribers('STORAGE_UPDATED', null) return { data: { user: null, session: null, redirectType: null }, error } } @@ -721,6 +727,8 @@ export default class GoTrueClient { } throw new AuthInvalidCredentialsError('You must provide either an email or phone number.') } catch (error) { + await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) + await this._notifyAllSubscribers('STORAGE_UPDATED', null) if (isAuthError(error)) { return { data: { user: null, session: null }, error } } @@ -820,6 +828,8 @@ export default class GoTrueClient { xform: _ssoResponse, }) } catch (error) { + await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) + await this._notifyAllSubscribers('STORAGE_UPDATED', null) if (isAuthError(error)) { return { data: null, error } } @@ -1221,6 +1231,7 @@ export default class GoTrueClient { emailRedirectTo?: string | undefined } = {} ): Promise { + let session: Session | null = null try { return await this._useSession(async (result) => { const { data: sessionData, error: sessionError } = result @@ -1230,7 +1241,7 @@ export default class GoTrueClient { if (!sessionData.session) { throw new AuthSessionMissingError() } - const session: Session = sessionData.session + session = sessionData.session let codeChallenge: string | null = null let codeChallengeMethod: string | null = null if (this.flowType === 'pkce' && attributes.email != null) { @@ -1258,6 +1269,8 @@ export default class GoTrueClient { return { data: { user: session.user }, error: null } }) } catch (error) { + await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) + await this._notifyAllSubscribers('STORAGE_UPDATED', session) if (isAuthError(error)) { return { data: { user: null }, error } } @@ -1686,6 +1699,8 @@ export default class GoTrueClient { redirectTo: options.redirectTo, }) } catch (error) { + await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) + await this._notifyAllSubscribers('STORAGE_UPDATED', null) if (isAuthError(error)) { return { data: null, error } } @@ -1722,9 +1737,11 @@ export default class GoTrueClient { * This method supports the PKCE flow. */ async linkIdentity(credentials: SignInWithOAuthCredentials): Promise { + let session: Session | null = null try { const { data, error } = await this._useSession(async (result) => { const { data, error } = result + session = data.session if (error) throw error const url: string = await this._getUrlForProvider( `${this.url}/user/identities/authorize`, @@ -1747,6 +1764,8 @@ export default class GoTrueClient { } return { data: { provider: credentials.provider, url: data?.url }, error: null } } catch (error) { + await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) + await this._notifyAllSubscribers('STORAGE_UPDATED', session) if (isAuthError(error)) { return { data: { provider: credentials.provider, url: null }, error } } @@ -2032,6 +2051,9 @@ export default class GoTrueClient { // so we can safely suppress the warning returned by future getSession calls this.suppressGetSessionWarning = true await setItemAsync(this.storage, this.storageKey, session) + + // cleanup potentially unused code verifier + await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) } private async _removeSession() { diff --git a/src/lib/types.ts b/src/lib/types.ts index 6d947c75..d4b11ed3 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -39,6 +39,7 @@ export type AuthChangeEvent = | 'PASSWORD_RECOVERY' | 'SIGNED_IN' | 'SIGNED_OUT' + | 'STORAGE_UPDATED' | 'TOKEN_REFRESHED' | 'USER_UPDATED' | AuthChangeEventMFA From a8a245f52dae679b5b2b22ce35941d5e4458acb8 Mon Sep 17 00:00:00 2001 From: Jason Creviston Date: Sat, 15 Mar 2025 19:06:13 -0400 Subject: [PATCH 2/3] refactor: remove STORAGE_UPDATED event and references --- src/GoTrueClient.ts | 8 -------- src/lib/types.ts | 1 - 2 files changed, 9 deletions(-) diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index f0b50a52..3bd587c9 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -487,7 +487,6 @@ export default class GoTrueClient { if (error || !data) { await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) - await this._notifyAllSubscribers('STORAGE_UPDATED', null) return { data: { user: null, session: null }, error: error } } @@ -502,7 +501,6 @@ export default class GoTrueClient { return { data: { user, session }, error: null } } catch (error) { await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) - await this._notifyAllSubscribers('STORAGE_UPDATED', null) if (isAuthError(error)) { return { data: { user: null, session: null }, error } } @@ -644,7 +642,6 @@ export default class GoTrueClient { } catch (error) { if (isAuthError(error)) { await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) - await this._notifyAllSubscribers('STORAGE_UPDATED', null) return { data: { user: null, session: null, redirectType: null }, error } } @@ -754,7 +751,6 @@ export default class GoTrueClient { throw new AuthInvalidCredentialsError('You must provide either an email or phone number.') } catch (error) { await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) - await this._notifyAllSubscribers('STORAGE_UPDATED', null) if (isAuthError(error)) { return { data: { user: null, session: null }, error } } @@ -855,7 +851,6 @@ export default class GoTrueClient { }) } catch (error) { await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) - await this._notifyAllSubscribers('STORAGE_UPDATED', null) if (isAuthError(error)) { return { data: null, error } } @@ -1301,7 +1296,6 @@ export default class GoTrueClient { }) } catch (error) { await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) - await this._notifyAllSubscribers('STORAGE_UPDATED', session) if (isAuthError(error)) { return { data: { user: null }, error } } @@ -1733,7 +1727,6 @@ export default class GoTrueClient { }) } catch (error) { await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) - await this._notifyAllSubscribers('STORAGE_UPDATED', null) if (isAuthError(error)) { return { data: null, error } } @@ -1798,7 +1791,6 @@ export default class GoTrueClient { return { data: { provider: credentials.provider, url: data?.url }, error: null } } catch (error) { await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) - await this._notifyAllSubscribers('STORAGE_UPDATED', session) if (isAuthError(error)) { return { data: { provider: credentials.provider, url: null }, error } } diff --git a/src/lib/types.ts b/src/lib/types.ts index 1d4ace29..33879304 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -33,7 +33,6 @@ export type AuthChangeEvent = | 'PASSWORD_RECOVERY' | 'SIGNED_IN' | 'SIGNED_OUT' - | 'STORAGE_UPDATED' | 'TOKEN_REFRESHED' | 'USER_UPDATED' | AuthChangeEventMFA From ccc11aa5ed1072665b279b60c47af12ed152f333 Mon Sep 17 00:00:00 2001 From: Jason Creviston Date: Sat, 15 Mar 2025 19:17:31 -0400 Subject: [PATCH 3/3] refactor: remove session changes tied to STORAGE_UPDATED event --- src/GoTrueClient.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index 3bd587c9..127f4e8f 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -1257,7 +1257,6 @@ export default class GoTrueClient { emailRedirectTo?: string | undefined } = {} ): Promise { - let session: Session | null = null try { return await this._useSession(async (result) => { const { data: sessionData, error: sessionError } = result @@ -1267,7 +1266,7 @@ export default class GoTrueClient { if (!sessionData.session) { throw new AuthSessionMissingError() } - session = sessionData.session + const session: Session = sessionData.session let codeChallenge: string | null = null let codeChallengeMethod: string | null = null if (this.flowType === 'pkce' && attributes.email != null) { @@ -1763,11 +1762,9 @@ export default class GoTrueClient { * This method supports the PKCE flow. */ async linkIdentity(credentials: SignInWithOAuthCredentials): Promise { - let session: Session | null = null try { const { data, error } = await this._useSession(async (result) => { const { data, error } = result - session = data.session if (error) throw error const url: string = await this._getUrlForProvider( `${this.url}/user/identities/authorize`, @@ -2076,8 +2073,6 @@ export default class GoTrueClient { // so we can safely suppress the warning returned by future getSession calls this.suppressGetSessionWarning = true await setItemAsync(this.storage, this.storageKey, session) - - // cleanup potentially unused code verifier await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) }