From f3bf1ab7a0a974056c4198a9c1edfc15d12ba8b6 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Wed, 15 Jan 2025 07:45:17 +1300 Subject: [PATCH 1/8] Support throwing errors instead of returning them Fixes #1020 Add support for `throwOnError` option in `AuthClient` to throw errors instead of returning them. * **GoTrueClient Changes:** - Add `throwOnError` option to `GoTrueClient` constructor. - Modify methods to throw errors when `throwOnError` is true. - Update methods like `signUp`, `signInWithPassword`, `updateUser`, and others to throw errors if `throwOnError` is true. * **AuthClient Changes:** - Update `AuthClient` to pass `throwOnError` option to `GoTrueClient`. * **Tests:** - Add tests in `test/GoTrueClient.test.ts` to verify `throwOnError` functionality. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/supabase/auth-js/issues/1020?shareId=XXXX-XXXX-XXXX-XXXX). --- src/AuthClient.ts | 7 ++- src/GoTrueClient.ts | 61 +++++++++++++++++++-- test/GoTrueClient.test.ts | 112 ++++++++++++++++++++++++-------------- 3 files changed, 134 insertions(+), 46 deletions(-) diff --git a/src/AuthClient.ts b/src/AuthClient.ts index 5677de450..ff00ced44 100644 --- a/src/AuthClient.ts +++ b/src/AuthClient.ts @@ -1,5 +1,10 @@ import GoTrueClient from './GoTrueClient' -const AuthClient = GoTrueClient +const AuthClient = (options) => { + return new GoTrueClient({ + ...options, + throwOnError: options.throwOnError || false, + }) +} export default AuthClient diff --git a/src/GoTrueClient.ts b/src/GoTrueClient.ts index 0b7d5d0d2..749240a94 100644 --- a/src/GoTrueClient.ts +++ b/src/GoTrueClient.ts @@ -107,6 +107,7 @@ const DEFAULT_OPTIONS: Omit, 'fetch' | 'storage' | flowType: 'implicit', debug: false, hasCustomAuthorizationHeader: false, + throwOnError: false, } /** Current session will be checked for refresh at this interval. */ @@ -167,6 +168,7 @@ export default class GoTrueClient { protected lock: LockFunc protected lockAcquired = false protected pendingInLock: Promise[] = [] + protected throwOnError: boolean /** * Used to broadcast state change events to other tabs listening. @@ -212,6 +214,7 @@ export default class GoTrueClient { this.detectSessionInUrl = settings.detectSessionInUrl this.flowType = settings.flowType this.hasCustomAuthorizationHeader = settings.hasCustomAuthorizationHeader + this.throwOnError = settings.throwOnError if (settings.lock) { this.lock = settings.lock @@ -399,6 +402,7 @@ export default class GoTrueClient { const { data, error } = res if (error || !data) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error: error } } const session: Session | null = data.session @@ -412,6 +416,7 @@ export default class GoTrueClient { return { data: { user, session }, error: null } } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } @@ -477,6 +482,7 @@ export default class GoTrueClient { const { data, error } = res if (error || !data) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error: error } } @@ -491,6 +497,7 @@ export default class GoTrueClient { return { data: { user, session }, error: null } } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } @@ -541,9 +548,12 @@ export default class GoTrueClient { const { data, error } = res if (error) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } else if (!data || !data.session || !data.user) { - return { data: { user: null, session: null }, error: new AuthInvalidTokenResponseError() } + const invalidTokenError = new AuthInvalidTokenResponseError() + if (this.throwOnError) throw invalidTokenError + return { data: { user: null, session: null }, error: invalidTokenError } } if (data.session) { await this._saveSession(data.session) @@ -559,6 +569,7 @@ export default class GoTrueClient { } } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } throw error @@ -659,11 +670,14 @@ export default class GoTrueClient { const { data, error } = res if (error) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } else if (!data || !data.session || !data.user) { + const invalidTokenError = new AuthInvalidTokenResponseError() + if (this.throwOnError) throw invalidTokenError return { data: { user: null, session: null }, - error: new AuthInvalidTokenResponseError(), + error: invalidTokenError, } } if (data.session) { @@ -673,6 +687,7 @@ export default class GoTrueClient { return { data, error } } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } throw error @@ -720,6 +735,7 @@ export default class GoTrueClient { }, redirectTo: options?.emailRedirectTo, }) + if (this.throwOnError && error) throw error return { data: { user: null, session: null }, error } } if ('phone' in credentials) { @@ -734,11 +750,13 @@ export default class GoTrueClient { channel: options?.channel ?? 'sms', }, }) + if (this.throwOnError && error) throw error return { data: { user: null, session: null, messageId: data?.message_id }, error } } throw new AuthInvalidCredentialsError('You must provide either an email or phone number.') } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } @@ -768,11 +786,14 @@ export default class GoTrueClient { }) if (error) { + if (this.throwOnError) throw error throw error } if (!data) { - throw new Error('An error occurred on token verification.') + const tokenVerificationError = new Error('An error occurred on token verification.') + if (this.throwOnError) throw tokenVerificationError + throw tokenVerificationError } const session: Session | null = data.session @@ -789,6 +810,7 @@ export default class GoTrueClient { return { data: { user, session }, error: null } } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } @@ -838,6 +860,7 @@ export default class GoTrueClient { }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: null, error } } throw error @@ -870,10 +893,12 @@ export default class GoTrueClient { headers: this.headers, jwt: session.access_token, }) + if (this.throwOnError && error) throw error return { data: { user: null, session: null }, error } }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } throw error @@ -897,6 +922,7 @@ export default class GoTrueClient { }, redirectTo: options?.emailRedirectTo, }) + if (this.throwOnError && error) throw error return { data: { user: null, session: null }, error } } else if ('phone' in credentials) { const { phone, type, options } = credentials @@ -908,6 +934,7 @@ export default class GoTrueClient { gotrue_meta_security: { captcha_token: options?.captchaToken }, }, }) + if (this.throwOnError && error) throw error return { data: { user: null, session: null, messageId: data?.message_id }, error } } throw new AuthInvalidCredentialsError( @@ -915,6 +942,7 @@ export default class GoTrueClient { ) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } throw error @@ -1209,6 +1237,7 @@ export default class GoTrueClient { await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`) } + if (this.throwOnError) throw error return { data: { user: null }, error } } @@ -1268,7 +1297,10 @@ export default class GoTrueClient { jwt: session.access_token, xform: _userResponse, }) - if (userError) throw userError + if (userError) { + if (this.throwOnError) throw userError + throw userError + } session.user = data.user as User await this._saveSession(session) await this._notifyAllSubscribers('USER_UPDATED', session) @@ -1276,6 +1308,7 @@ export default class GoTrueClient { }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null }, error } } @@ -1334,6 +1367,7 @@ export default class GoTrueClient { currentSession.refresh_token ) if (error) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error: error } } @@ -1361,6 +1395,7 @@ export default class GoTrueClient { return { data: { user: session.user, session }, error: null } } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { session: null, user: null }, error } } @@ -1402,6 +1437,7 @@ export default class GoTrueClient { const { session, error } = await this._callRefreshToken(currentSession.refresh_token) if (error) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error: error } } @@ -1413,6 +1449,7 @@ export default class GoTrueClient { }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { user: null, session: null }, error } } @@ -1547,6 +1584,7 @@ export default class GoTrueClient { return { data: { session, redirectType: params.type }, error: null } } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { session: null, redirectType: null }, error } } @@ -1609,6 +1647,7 @@ export default class GoTrueClient { (error.status === 404 || error.status === 401 || error.status === 403) ) ) { + if (this.throwOnError) throw error return { error } } } @@ -1717,6 +1756,7 @@ export default class GoTrueClient { }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: null, error } } @@ -1742,6 +1782,7 @@ export default class GoTrueClient { return { data: { identities: data.user.identities ?? [] }, error: null } } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: null, error } } throw error @@ -1778,6 +1819,7 @@ export default class GoTrueClient { return { data: { provider: credentials.provider, url: data?.url }, error: null } } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { provider: credentials.provider, url: null }, error } } throw error @@ -1812,6 +1854,7 @@ export default class GoTrueClient { }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: null, error } } throw error @@ -1858,6 +1901,7 @@ export default class GoTrueClient { this._debug(debugName, 'error', error) if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: { session: null, user: null }, error } } throw error @@ -2373,6 +2417,7 @@ export default class GoTrueClient { }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: null, error } } throw error @@ -2405,6 +2450,7 @@ export default class GoTrueClient { }) if (error) { + if (this.throwOnError) throw error return { data: null, error } } @@ -2416,6 +2462,7 @@ export default class GoTrueClient { }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: null, error } } throw error @@ -2445,6 +2492,7 @@ export default class GoTrueClient { } ) if (error) { + if (this.throwOnError) throw error return { data: null, error } } @@ -2458,6 +2506,7 @@ export default class GoTrueClient { }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: null, error } } throw error @@ -2490,6 +2539,7 @@ export default class GoTrueClient { }) } catch (error) { if (isAuthError(error)) { + if (this.throwOnError) throw error return { data: null, error } } throw error @@ -2510,6 +2560,7 @@ export default class GoTrueClient { factorId: params.factorId, }) if (challengeError) { + if (this.throwOnError) throw challengeError return { data: null, error: challengeError } } @@ -2530,6 +2581,7 @@ export default class GoTrueClient { error: userError, } = await this.getUser() if (userError) { + if (this.throwOnError) throw userError return { data: null, error: userError } } @@ -2562,6 +2614,7 @@ export default class GoTrueClient { error: sessionError, } = result if (sessionError) { + if (this.throwOnError) throw sessionError return { data: null, error: sessionError } } if (!session) { diff --git a/test/GoTrueClient.test.ts b/test/GoTrueClient.test.ts index 7d426513d..0f78297d7 100644 --- a/test/GoTrueClient.test.ts +++ b/test/GoTrueClient.test.ts @@ -90,7 +90,7 @@ describe('GoTrueClient', () => { expect(session!.token_type).toStrictEqual('bearer') expect(refreshAccessTokenSpy).toBeCalledTimes(1) // @ts-expect-error 'data.session and session should not be null because of the assertion above' - expect(data.session.refresh_token).not.toEqual(session.refresh_token) + expect(data.session.refresh_token).not toEqual(session.refresh_token) }) test('setSession should return no error', async () => { @@ -101,7 +101,7 @@ describe('GoTrueClient', () => { password, }) expect(error).toBeNull() - expect(data.session).not.toBeNull() + expect(data.session).not toBeNull() const { data: { session }, @@ -113,12 +113,12 @@ describe('GoTrueClient', () => { refresh_token: data.session.refresh_token, }) expect(setSessionError).toBeNull() - expect(session).not.toBeNull() - expect(session!.user).not.toBeNull() - expect(session!.expires_in).not.toBeNull() - expect(session!.expires_at).not.toBeNull() - expect(session!.access_token).not.toBeNull() - expect(session!.refresh_token).not.toBeNull() + expect(session).not toBeNull() + expect(session!.user).not toBeNull() + expect(session!.expires_in).not toBeNull() + expect(session!.expires_at).not toBeNull() + expect(session!.access_token).not toBeNull() + expect(session!.refresh_token).not toBeNull() expect(session!.token_type).toStrictEqual('bearer') /** @@ -127,7 +127,7 @@ describe('GoTrueClient', () => { */ const { data: getSessionData, error: getSessionError } = await authWithSession.getSession() expect(getSessionError).toBeNull() - expect(getSessionData).not.toBeNull() + expect(getSessionData).not toBeNull() const { data: { user }, @@ -135,7 +135,7 @@ describe('GoTrueClient', () => { } = await authWithSession.updateUser({ data: { hello: 'world' } }) expect(updateError).toBeNull() - expect(user).not.toBeNull() + expect(user).not toBeNull() expect(user?.user_metadata).toMatchObject({ hello: 'world' }) }) @@ -148,12 +148,12 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not.toBeNull() + expect(data.session).not toBeNull() const { data: userSession, error: userError } = await authWithSession.getSession() expect(userError).toBeNull() - expect(userSession.session).not.toBeNull() + expect(userSession.session).not toBeNull() expect(userSession.session).toHaveProperty('access_token') expect(userSession.session).toHaveProperty('user') }) @@ -167,7 +167,7 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not.toBeNull() + expect(data.session).not toBeNull() const expired = new Date() expired.setMinutes(expired.getMinutes() - 1) @@ -192,10 +192,10 @@ describe('GoTrueClient', () => { const { data: userSession, error: userError } = await authWithSession.getSession() expect(userError).toBeNull() - expect(userSession.session).not.toBeNull() + expect(userSession.session).not toBeNull() expect(userSession.session).toHaveProperty('access_token') expect(refreshAccessTokenSpy).toBeCalledTimes(1) - expect(data.session?.access_token).not.toEqual(userSession.session?.access_token) + expect(data.session?.access_token).not toEqual(userSession.session?.access_token) }) test('refresh should only happen once', async () => { @@ -207,7 +207,7 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not.toBeNull() + expect(data.session).not toBeNull() const [{ session: session1, error: error1 }, { session: session2, error: error2 }] = await Promise.all([ @@ -245,7 +245,7 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not.toBeNull() + expect(data.session).not toBeNull() const [{ session: session1, error: error1 }, { session: session2, error: error2 }] = await Promise.all([ @@ -286,7 +286,7 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not.toBeNull() + expect(data.session).not toBeNull() const [error1, error2] = await Promise.allSettled([ // @ts-expect-error 'Allow access to private _callRefreshToken()' @@ -336,8 +336,8 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not.toBeNull() - expect(data.user).not.toBeNull() + expect(data.session).not toBeNull() + expect(data.user).not toBeNull() expect(data.user?.email).toEqual(email) }) @@ -352,7 +352,7 @@ describe('GoTrueClient', () => { password, }) - expect(error).not.toBeNull() + expect(error).not toBeNull() expect(data.session).toBeNull() expect(data.user).toBeNull() @@ -368,7 +368,7 @@ describe('GoTrueClient', () => { password, }) - expect(error).not.toBeNull() + expect(error).not toBeNull() expect(data.session).toBeNull() expect(data.user).toBeNull() }) @@ -379,7 +379,7 @@ describe('GoTrueClient', () => { const { data, error } = await phoneClient.signInWithOtp({ phone, }) - expect(error).not.toBeNull() + expect(error).not toBeNull() expect(data.session).toBeNull() expect(data.user).toBeNull() }) @@ -509,7 +509,7 @@ describe('GoTrueClient', () => { expect(initialError).toBeNull() const initialSession = data.session - expect(initialSession).not.toBeNull() + expect(initialSession).not toBeNull() const { data: userSession, error } = await authWithSession.getSession() @@ -547,7 +547,7 @@ describe('GoTrueClient', () => { }, }) - expect(userSession.session?.user).not.toBeNull() + expect(userSession.session?.user).not toBeNull() expect(userSession.session?.user?.email).toBe(email) }) }) @@ -591,13 +591,13 @@ describe('Signout behaviour', () => { password, }) expect(signInError).toBe(null) - expect(user).not.toBe(null) + expect(user).not toBe(null) const { data: { session }, error: sessionError, } = await authWithSession.getSession() - expect(session).not.toBe(null) + expect(session).not toBe(null) expect(sessionError).toBe(null) const id = user ? user.id : '' // user should not be null @@ -664,7 +664,7 @@ describe('User management', () => { }, }) - expect(user).not.toBeNull() + expect(user).not toBeNull() expect(user?.email).toBe(email) expect(user?.user_metadata).toMatchObject(userMetadata) }) @@ -712,7 +712,7 @@ describe('User management', () => { password, }) - expect(data.user).not.toBeNull() + expect(data.user).not toBeNull() await authWithSession.signOut() const { data: userSession, error } = await authWithSession.getSession() @@ -728,8 +728,8 @@ describe('User management', () => { password: password + '-wrong', }) - expect(error).not.toBeNull() - expect(error?.message).not.toBeNull() + expect(error).not toBeNull() + expect(error?.message).not toBeNull() expect(data.session).toBeNull() }) }) @@ -824,7 +824,7 @@ describe('The auth client can signin with third-party oAuth providers', () => { }) expect(error).toBeNull() - expect(user).not.toBeNull() + expect(user).not toBeNull() expect(user?.email).toEqual(email) }) @@ -843,7 +843,7 @@ describe('The auth client can signin with third-party oAuth providers', () => { }) expect(user).toBeNull() - expect(error).not.toBeNull() + expect(error).not toBeNull() expect(error?.message).toEqual('Signups not allowed for this instance') }) }) @@ -859,7 +859,7 @@ describe('User management', () => { }) expect(initialError).toBeNull() - expect(data.session).not.toBeNull() + expect(data.session).not toBeNull() const redirectTo = 'http://localhost:9999/welcome' const { error, data: user } = await authWithSession.resetPasswordForEmail(email, { @@ -899,7 +899,7 @@ describe('User management', () => { const user = refreshedSession?.user expect(error).toBeNull() - expect(user).not.toBeNull() + expect(user).not toBeNull() expect(user?.email).toEqual(email) }) }) @@ -914,7 +914,7 @@ describe('MFA', () => { throw error } - expect(data.totp.qr_code).not.toBeNull() + expect(data.totp.qr_code).not toBeNull() }) }) @@ -982,7 +982,7 @@ describe('GoTrueClient with storageisServer = true', () => { } = await client.getSession() const user = session?.user // accessing the user object from getSession should emit a warning the first time - expect(user).not.toBeNull() + expect(user).not toBeNull() expect(warnings.length).toEqual(1) expect( warnings[0][0].startsWith( @@ -991,7 +991,7 @@ describe('GoTrueClient with storageisServer = true', () => { ).toEqual(true) const user2 = session?.user // accessing the user object further should not emit a warning - expect(user2).not.toBeNull() + expect(user2).not toBeNull() expect(warnings.length).toEqual(1) const { @@ -999,7 +999,7 @@ describe('GoTrueClient with storageisServer = true', () => { } = await client.getSession() // create new proxy instance const user3 = session2?.user // accessing the user object in subsequent proxy instances, for this client, should not emit a warning - expect(user3).not.toBeNull() + expect(user3).not toBeNull() expect(warnings.length).toEqual(1) }) @@ -1021,14 +1021,14 @@ describe('GoTrueClient with storageisServer = true', () => { error, } = await client.getUser() // should suppress any warnings expect(error).toBeNull() - expect(user).not.toBeNull() + expect(user).not toBeNull() const { data: { session }, } = await client.getSession() const sessionUser = session?.user // accessing the user object from getSession shouldn't emit a warning - expect(sessionUser).not.toBeNull() + expect(sessionUser).not toBeNull() expect(warnings.length).toEqual(0) }) @@ -1080,3 +1080,33 @@ describe('GoTrueClient with storageisServer = true', () => { expect(store.getItem('test-storage-key')).toEqual(JSON.stringify(newSession)) }) }) + +describe('GoTrueClient with throwOnError option', () => { + const client = new GoTrueClient({ + url: GOTRUE_URL_SIGNUP_ENABLED_AUTO_CONFIRM_ON, + autoRefreshToken: false, + persistSession: true, + storage: new MemoryStorage(), + throwOnError: true, + }) + + test('signUp() should throw an error when throwOnError is true', async () => { + const { email, password } = mockUserCredentials() + + await expect(client.signUp({ email, password: '' })).rejects.toThrow() + }) + + test('signInWithPassword() should throw an error when throwOnError is true', async () => { + const { email, password } = mockUserCredentials() + + await expect(client.signInWithPassword({ email, password: '' })).rejects.toThrow() + }) + + test('updateUser() should throw an error when throwOnError is true', async () => { + const { email, password } = mockUserCredentials() + + await client.signUp({ email, password }) + + await expect(client.updateUser({ email: 'invalid-email' })).rejects.toThrow() + }) +}) From 4806ba128f2409bdccadaa796267af09089c4381 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Wed, 15 Jan 2025 07:58:40 +1300 Subject: [PATCH 2/8] Update test assertions to use correct syntax with periods * Correct syntax for `expect` assertions to use `.not.toBeNull()` and `.not.toEqual()` instead of `not toBeNull` and `not toEqual` --- test/GoTrueClient.test.ts | 82 +++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/test/GoTrueClient.test.ts b/test/GoTrueClient.test.ts index 0f78297d7..6652113c8 100644 --- a/test/GoTrueClient.test.ts +++ b/test/GoTrueClient.test.ts @@ -90,7 +90,7 @@ describe('GoTrueClient', () => { expect(session!.token_type).toStrictEqual('bearer') expect(refreshAccessTokenSpy).toBeCalledTimes(1) // @ts-expect-error 'data.session and session should not be null because of the assertion above' - expect(data.session.refresh_token).not toEqual(session.refresh_token) + expect(data.session.refresh_token).not.toEqual(session.refresh_token) }) test('setSession should return no error', async () => { @@ -101,7 +101,7 @@ describe('GoTrueClient', () => { password, }) expect(error).toBeNull() - expect(data.session).not toBeNull() + expect(data.session).not.toBeNull() const { data: { session }, @@ -113,12 +113,12 @@ describe('GoTrueClient', () => { refresh_token: data.session.refresh_token, }) expect(setSessionError).toBeNull() - expect(session).not toBeNull() - expect(session!.user).not toBeNull() - expect(session!.expires_in).not toBeNull() - expect(session!.expires_at).not toBeNull() - expect(session!.access_token).not toBeNull() - expect(session!.refresh_token).not toBeNull() + expect(session).not.toBeNull() + expect(session!.user).not.toBeNull() + expect(session!.expires_in).not.toBeNull() + expect(session!.expires_at).not.toBeNull() + expect(session!.access_token).not.toBeNull() + expect(session!.refresh_token).not.toBeNull() expect(session!.token_type).toStrictEqual('bearer') /** @@ -127,7 +127,7 @@ describe('GoTrueClient', () => { */ const { data: getSessionData, error: getSessionError } = await authWithSession.getSession() expect(getSessionError).toBeNull() - expect(getSessionData).not toBeNull() + expect(getSessionData).not.toBeNull() const { data: { user }, @@ -135,7 +135,7 @@ describe('GoTrueClient', () => { } = await authWithSession.updateUser({ data: { hello: 'world' } }) expect(updateError).toBeNull() - expect(user).not toBeNull() + expect(user).not.toBeNull() expect(user?.user_metadata).toMatchObject({ hello: 'world' }) }) @@ -148,12 +148,12 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not toBeNull() + expect(data.session).not.toBeNull() const { data: userSession, error: userError } = await authWithSession.getSession() expect(userError).toBeNull() - expect(userSession.session).not toBeNull() + expect(userSession.session).not.toBeNull() expect(userSession.session).toHaveProperty('access_token') expect(userSession.session).toHaveProperty('user') }) @@ -167,7 +167,7 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not toBeNull() + expect(data.session).not.toBeNull() const expired = new Date() expired.setMinutes(expired.getMinutes() - 1) @@ -192,10 +192,10 @@ describe('GoTrueClient', () => { const { data: userSession, error: userError } = await authWithSession.getSession() expect(userError).toBeNull() - expect(userSession.session).not toBeNull() + expect(userSession.session).not.toBeNull() expect(userSession.session).toHaveProperty('access_token') expect(refreshAccessTokenSpy).toBeCalledTimes(1) - expect(data.session?.access_token).not toEqual(userSession.session?.access_token) + expect(data.session?.access_token).not.toEqual(userSession.session?.access_token) }) test('refresh should only happen once', async () => { @@ -207,7 +207,7 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not toBeNull() + expect(data.session).not.toBeNull() const [{ session: session1, error: error1 }, { session: session2, error: error2 }] = await Promise.all([ @@ -245,7 +245,7 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not toBeNull() + expect(data.session).not.toBeNull() const [{ session: session1, error: error1 }, { session: session2, error: error2 }] = await Promise.all([ @@ -286,7 +286,7 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not toBeNull() + expect(data.session).not.toBeNull() const [error1, error2] = await Promise.allSettled([ // @ts-expect-error 'Allow access to private _callRefreshToken()' @@ -336,8 +336,8 @@ describe('GoTrueClient', () => { }) expect(error).toBeNull() - expect(data.session).not toBeNull() - expect(data.user).not toBeNull() + expect(data.session).not.toBeNull() + expect(data.user).not.toBeNull() expect(data.user?.email).toEqual(email) }) @@ -352,7 +352,7 @@ describe('GoTrueClient', () => { password, }) - expect(error).not toBeNull() + expect(error).not.toBeNull() expect(data.session).toBeNull() expect(data.user).toBeNull() @@ -368,7 +368,7 @@ describe('GoTrueClient', () => { password, }) - expect(error).not toBeNull() + expect(error).not.toBeNull() expect(data.session).toBeNull() expect(data.user).toBeNull() }) @@ -379,7 +379,7 @@ describe('GoTrueClient', () => { const { data, error } = await phoneClient.signInWithOtp({ phone, }) - expect(error).not toBeNull() + expect(error).not.toBeNull() expect(data.session).toBeNull() expect(data.user).toBeNull() }) @@ -509,7 +509,7 @@ describe('GoTrueClient', () => { expect(initialError).toBeNull() const initialSession = data.session - expect(initialSession).not toBeNull() + expect(initialSession).not.toBeNull() const { data: userSession, error } = await authWithSession.getSession() @@ -547,7 +547,7 @@ describe('GoTrueClient', () => { }, }) - expect(userSession.session?.user).not toBeNull() + expect(userSession.session?.user).not.toBeNull() expect(userSession.session?.user?.email).toBe(email) }) }) @@ -591,13 +591,13 @@ describe('Signout behaviour', () => { password, }) expect(signInError).toBe(null) - expect(user).not toBe(null) + expect(user).not.toBe(null) const { data: { session }, error: sessionError, } = await authWithSession.getSession() - expect(session).not toBe(null) + expect(session).not.toBe(null) expect(sessionError).toBe(null) const id = user ? user.id : '' // user should not be null @@ -664,7 +664,7 @@ describe('User management', () => { }, }) - expect(user).not toBeNull() + expect(user).not.toBeNull() expect(user?.email).toBe(email) expect(user?.user_metadata).toMatchObject(userMetadata) }) @@ -712,7 +712,7 @@ describe('User management', () => { password, }) - expect(data.user).not toBeNull() + expect(data.user).not.toBeNull() await authWithSession.signOut() const { data: userSession, error } = await authWithSession.getSession() @@ -728,8 +728,8 @@ describe('User management', () => { password: password + '-wrong', }) - expect(error).not toBeNull() - expect(error?.message).not toBeNull() + expect(error).not.toBeNull() + expect(error?.message).not.toBeNull() expect(data.session).toBeNull() }) }) @@ -824,7 +824,7 @@ describe('The auth client can signin with third-party oAuth providers', () => { }) expect(error).toBeNull() - expect(user).not toBeNull() + expect(user).not.toBeNull() expect(user?.email).toEqual(email) }) @@ -843,7 +843,7 @@ describe('The auth client can signin with third-party oAuth providers', () => { }) expect(user).toBeNull() - expect(error).not toBeNull() + expect(error).not.toBeNull() expect(error?.message).toEqual('Signups not allowed for this instance') }) }) @@ -859,7 +859,7 @@ describe('User management', () => { }) expect(initialError).toBeNull() - expect(data.session).not toBeNull() + expect(data.session).not.toBeNull() const redirectTo = 'http://localhost:9999/welcome' const { error, data: user } = await authWithSession.resetPasswordForEmail(email, { @@ -899,7 +899,7 @@ describe('User management', () => { const user = refreshedSession?.user expect(error).toBeNull() - expect(user).not toBeNull() + expect(user).not.toBeNull() expect(user?.email).toEqual(email) }) }) @@ -914,7 +914,7 @@ describe('MFA', () => { throw error } - expect(data.totp.qr_code).not toBeNull() + expect(data.totp.qr_code).not.toBeNull() }) }) @@ -982,7 +982,7 @@ describe('GoTrueClient with storageisServer = true', () => { } = await client.getSession() const user = session?.user // accessing the user object from getSession should emit a warning the first time - expect(user).not toBeNull() + expect(user).not.toBeNull() expect(warnings.length).toEqual(1) expect( warnings[0][0].startsWith( @@ -991,7 +991,7 @@ describe('GoTrueClient with storageisServer = true', () => { ).toEqual(true) const user2 = session?.user // accessing the user object further should not emit a warning - expect(user2).not toBeNull() + expect(user2).not.toBeNull() expect(warnings.length).toEqual(1) const { @@ -999,7 +999,7 @@ describe('GoTrueClient with storageisServer = true', () => { } = await client.getSession() // create new proxy instance const user3 = session2?.user // accessing the user object in subsequent proxy instances, for this client, should not emit a warning - expect(user3).not toBeNull() + expect(user3).not.toBeNull() expect(warnings.length).toEqual(1) }) @@ -1021,14 +1021,14 @@ describe('GoTrueClient with storageisServer = true', () => { error, } = await client.getUser() // should suppress any warnings expect(error).toBeNull() - expect(user).not toBeNull() + expect(user).not.toBeNull() const { data: { session }, } = await client.getSession() const sessionUser = session?.user // accessing the user object from getSession shouldn't emit a warning - expect(sessionUser).not toBeNull() + expect(sessionUser).not.toBeNull() expect(warnings.length).toEqual(0) }) From ed8d296feb2fa6745eb2d323697f6155785ac26b Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Thu, 16 Jan 2025 18:49:58 +1300 Subject: [PATCH 3/8] remove AI suggestions --- src/AuthClient.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/AuthClient.ts b/src/AuthClient.ts index ff00ced44..5677de450 100644 --- a/src/AuthClient.ts +++ b/src/AuthClient.ts @@ -1,10 +1,5 @@ import GoTrueClient from './GoTrueClient' -const AuthClient = (options) => { - return new GoTrueClient({ - ...options, - throwOnError: options.throwOnError || false, - }) -} +const AuthClient = GoTrueClient export default AuthClient From 0f6fdff529bb6569bcf0e86eca3c41d399d82f46 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Thu, 16 Jan 2025 18:53:38 +1300 Subject: [PATCH 4/8] Adds to the type --- src/lib/types.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/types.ts b/src/lib/types.ts index 31b117ff8..5169fdabd 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -86,6 +86,11 @@ export type GoTrueClientOptions = { * @experimental */ hasCustomAuthorizationHeader?: boolean + ** + * If there is an error with the query, throwOnError will reject the promise by + * throwing the error instead of returning it as part of a successful response. + */ + throwOnError?: false } export type WeakPasswordReasons = 'length' | 'characters' | 'pwned' | (string & {}) From ea2fd39718304df718aa28f206b0c80b616a82ec Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Thu, 16 Jan 2025 18:54:47 +1300 Subject: [PATCH 5/8] fixes spelling mistake --- src/lib/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/types.ts b/src/lib/types.ts index 5169fdabd..6eb76423b 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -86,7 +86,7 @@ export type GoTrueClientOptions = { * @experimental */ hasCustomAuthorizationHeader?: boolean - ** + /** * If there is an error with the query, throwOnError will reject the promise by * throwing the error instead of returning it as part of a successful response. */ From 73a52a119fc554d6c5e1f7b49e10b91250af89e1 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:12:45 +1300 Subject: [PATCH 6/8] fixes storage error on test --- test/GoTrueClient.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/GoTrueClient.test.ts b/test/GoTrueClient.test.ts index 6652113c8..e86feb310 100644 --- a/test/GoTrueClient.test.ts +++ b/test/GoTrueClient.test.ts @@ -1082,11 +1082,15 @@ describe('GoTrueClient with storageisServer = true', () => { }) describe('GoTrueClient with throwOnError option', () => { + const store = memoryLocalStorageAdapter() const client = new GoTrueClient({ url: GOTRUE_URL_SIGNUP_ENABLED_AUTO_CONFIRM_ON, + storageKey: 'test-storage-key', autoRefreshToken: false, persistSession: true, - storage: new MemoryStorage(), + storage: { + ...store + }, throwOnError: true, }) From e15b402ff7cbdf30e905a637636a039f36c88bab Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:16:52 +1300 Subject: [PATCH 7/8] fixes type --- src/lib/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/types.ts b/src/lib/types.ts index 6eb76423b..500e53a95 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -90,7 +90,7 @@ export type GoTrueClientOptions = { * If there is an error with the query, throwOnError will reject the promise by * throwing the error instead of returning it as part of a successful response. */ - throwOnError?: false + throwOnError?: boolean } export type WeakPasswordReasons = 'length' | 'characters' | 'pwned' | (string & {}) From da467c2a218ed3c30063ab0becfb944bb1024edc Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:19:56 +1300 Subject: [PATCH 8/8] small bump to pr to fix errors --- test/GoTrueClient.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/GoTrueClient.test.ts b/test/GoTrueClient.test.ts index e86feb310..8f7065ef4 100644 --- a/test/GoTrueClient.test.ts +++ b/test/GoTrueClient.test.ts @@ -1091,7 +1091,7 @@ describe('GoTrueClient with throwOnError option', () => { storage: { ...store }, - throwOnError: true, + throwOnError: true, // test that the client throws errors }) test('signUp() should throw an error when throwOnError is true', async () => {