@supabase/gotrue-js 2.42.0 → 2.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,6 +29,8 @@ import {
29
29
  generatePKCEVerifier,
30
30
  generatePKCEChallenge,
31
31
  supportsLocalStorage,
32
+ stackGuard,
33
+ isInStackGuard,
32
34
  } from './lib/helpers'
33
35
  import localStorageAdapter from './lib/local-storage'
34
36
  import { polyfillGlobalThis } from './lib/polyfills'
@@ -235,11 +237,11 @@ export default class GoTrueClient {
235
237
  * manually when checking for an error from an auth redirect (oauth, magiclink, password recovery, etc).
236
238
  */
237
239
  initialize(): Promise<InitializeResult> {
238
- if (!this.initializePromise) {
239
- this.initializePromise = this._initialize()
240
+ if (this.initializePromise) {
241
+ return this.initializePromise
240
242
  }
241
243
 
242
- return this.initializePromise
244
+ return this._initialize()
243
245
  }
244
246
 
245
247
  /**
@@ -250,63 +252,66 @@ export default class GoTrueClient {
250
252
  */
251
253
  private async _initialize(): Promise<InitializeResult> {
252
254
  if (this.initializePromise) {
253
- return this.initializePromise
255
+ throw new Error('Double call of #_initialize()')
254
256
  }
255
257
 
256
- try {
257
- const isPKCEFlow = isBrowser() ? await this._isPKCEFlow() : false
258
- this._debug('#_initialize()', 'begin', 'is PKCE flow', isPKCEFlow)
258
+ this.initializePromise = stackGuard('_initialize', async () => {
259
+ try {
260
+ const isPKCEFlow = isBrowser() ? await this._isPKCEFlow() : false
261
+ this._debug('#_initialize()', 'begin', 'is PKCE flow', isPKCEFlow)
259
262
 
260
- if (isPKCEFlow || (this.detectSessionInUrl && this._isImplicitGrantFlow())) {
261
- const { data, error } = await this._getSessionFromUrl(isPKCEFlow)
262
- if (error) {
263
- this._debug('#_initialize()', 'error detecting session from URL', error)
263
+ if (isPKCEFlow || (this.detectSessionInUrl && this._isImplicitGrantFlow())) {
264
+ const { data, error } = await this._getSessionFromUrl(isPKCEFlow)
265
+ if (error) {
266
+ this._debug('#_initialize()', 'error detecting session from URL', error)
264
267
 
265
- // failed login attempt via url,
266
- // remove old session as in verifyOtp, signUp and signInWith*
267
- await this._removeSession()
268
+ // failed login attempt via url,
269
+ // remove old session as in verifyOtp, signUp and signInWith*
270
+ await this._removeSession()
268
271
 
269
- return { error }
270
- }
272
+ return { error }
273
+ }
271
274
 
272
- const { session, redirectType } = data
275
+ const { session, redirectType } = data
273
276
 
274
- this._debug(
275
- '#_initialize()',
276
- 'detected session in URL',
277
- session,
278
- 'redirect type',
279
- redirectType
280
- )
277
+ this._debug(
278
+ '#_initialize()',
279
+ 'detected session in URL',
280
+ session,
281
+ 'redirect type',
282
+ redirectType
283
+ )
281
284
 
282
- await this._saveSession(session)
285
+ await this._saveSession(session)
283
286
 
284
- setTimeout(async () => {
285
- if (redirectType === 'recovery') {
286
- await this._notifyAllSubscribers('PASSWORD_RECOVERY', session)
287
- } else {
288
- await this._notifyAllSubscribers('SIGNED_IN', session)
289
- }
290
- }, 0)
287
+ setTimeout(async () => {
288
+ if (redirectType === 'recovery') {
289
+ await this._notifyAllSubscribers('PASSWORD_RECOVERY', session)
290
+ } else {
291
+ await this._notifyAllSubscribers('SIGNED_IN', session)
292
+ }
293
+ }, 0)
291
294
 
295
+ return { error: null }
296
+ }
297
+ // no login attempt via callback url try to recover session from storage
298
+ await this._recoverAndRefresh()
292
299
  return { error: null }
293
- }
300
+ } catch (error) {
301
+ if (isAuthError(error)) {
302
+ return { error }
303
+ }
294
304
 
295
- // no login attempt via callback url try to recover session from storage
296
- await this._recoverAndRefresh()
297
- return { error: null }
298
- } catch (error) {
299
- if (isAuthError(error)) {
300
- return { error }
305
+ return {
306
+ error: new AuthUnknownError('Unexpected error during initialization', error),
307
+ }
308
+ } finally {
309
+ await this._handleVisibilityChange()
310
+ this._debug('#_initialize()', 'end')
301
311
  }
312
+ })
302
313
 
303
- return {
304
- error: new AuthUnknownError('Unexpected error during initialization', error),
305
- }
306
- } finally {
307
- await this._handleVisibilityChange()
308
- this._debug('#_initialize()', 'end')
309
- }
314
+ return await this.initializePromise
310
315
  }
311
316
 
312
317
  /**
@@ -699,18 +704,20 @@ export default class GoTrueClient {
699
704
  */
700
705
  async reauthenticate(): Promise<AuthResponse> {
701
706
  try {
702
- const {
703
- data: { session },
704
- error: sessionError,
705
- } = await this.getSession()
706
- if (sessionError) throw sessionError
707
- if (!session) throw new AuthSessionMissingError()
707
+ return await this._useSession(async (result) => {
708
+ const {
709
+ data: { session },
710
+ error: sessionError,
711
+ } = result
712
+ if (sessionError) throw sessionError
713
+ if (!session) throw new AuthSessionMissingError()
708
714
 
709
- const { error } = await _request(this.fetch, 'GET', `${this.url}/reauthenticate`, {
710
- headers: this.headers,
711
- jwt: session.access_token,
715
+ const { error } = await _request(this.fetch, 'GET', `${this.url}/reauthenticate`, {
716
+ headers: this.headers,
717
+ jwt: session.access_token,
718
+ })
719
+ return { data: { user: null, session: null }, error }
712
720
  })
713
- return { data: { user: null, session: null }, error }
714
721
  } catch (error) {
715
722
  if (isAuthError(error)) {
716
723
  return { data: { user: null, session: null }, error }
@@ -768,7 +775,55 @@ export default class GoTrueClient {
768
775
  * Returns the session, refreshing it if necessary.
769
776
  * The session returned can be null if the session is not detected which can happen in the event a user is not signed-in or has logged out.
770
777
  */
771
- async getSession(): Promise<
778
+ async getSession() {
779
+ return this._useSession(async (result) => {
780
+ return result
781
+ })
782
+ }
783
+
784
+ /**
785
+ * Use instead of {@link #getSession} inside the library. It is
786
+ * semantically usually what you want, as getting a session involves some
787
+ * processing afterwards that requires only one client operating on the
788
+ * session at once across multiple tabs or processes.
789
+ */
790
+ private async _useSession<R>(
791
+ fn: (
792
+ result:
793
+ | {
794
+ data: {
795
+ session: Session
796
+ }
797
+ error: null
798
+ }
799
+ | {
800
+ data: {
801
+ session: null
802
+ }
803
+ error: AuthError
804
+ }
805
+ | {
806
+ data: {
807
+ session: null
808
+ }
809
+ error: null
810
+ }
811
+ ) => Promise<R>
812
+ ): Promise<R> {
813
+ return await stackGuard('_useSession', async () => {
814
+ // the use of __loadSession here is the only correct use of the function!
815
+ const result = await this.__loadSession()
816
+
817
+ return await fn(result)
818
+ })
819
+ }
820
+
821
+ /**
822
+ * NEVER USE DIRECTLY!
823
+ *
824
+ * Always use {@link #_useSession}.
825
+ */
826
+ private async __loadSession(): Promise<
772
827
  | {
773
828
  data: {
774
829
  session: Session
@@ -788,11 +843,21 @@ export default class GoTrueClient {
788
843
  error: null
789
844
  }
790
845
  > {
846
+ this._debug('#__loadSession()', 'begin')
847
+
848
+ if (this.logDebugMessages && !isInStackGuard('_useSession')) {
849
+ throw new Error('Please use #_useSession()')
850
+ }
851
+
791
852
  // make sure we've read the session from the url if there is one
792
853
  // save to just await, as long we make sure _initialize() never throws
793
- await this.initializePromise
794
-
795
- this._debug('#getSession()', 'begin')
854
+ if (!isInStackGuard('_initialize')) {
855
+ // only wait when not called from within #_initialize() since it's
856
+ // waiting for itself. one such pathway is #_initialize() ->
857
+ // #_handleVisibilityChange() -> #_onVisbilityChanged() ->
858
+ // #_loadSession().
859
+ await this.initializePromise
860
+ }
796
861
 
797
862
  try {
798
863
  let currentSession: Session | null = null
@@ -824,7 +889,7 @@ export default class GoTrueClient {
824
889
  : false
825
890
 
826
891
  this._debug(
827
- '#getSession()',
892
+ '#__loadSession()',
828
893
  `session has${hasExpired ? '' : ' not'} expired`,
829
894
  'expires_at',
830
895
  currentSession.expires_at
@@ -841,7 +906,7 @@ export default class GoTrueClient {
841
906
 
842
907
  return { data: { session }, error: null }
843
908
  } finally {
844
- this._debug('#getSession()', 'end')
909
+ this._debug('#__loadSession()', 'end')
845
910
  }
846
911
  }
847
912
 
@@ -851,20 +916,22 @@ export default class GoTrueClient {
851
916
  */
852
917
  async getUser(jwt?: string): Promise<UserResponse> {
853
918
  try {
854
- if (!jwt) {
855
- const { data, error } = await this.getSession()
856
- if (error) {
857
- throw error
858
- }
919
+ return await this._useSession(async (result) => {
920
+ if (!jwt) {
921
+ const { data, error } = result
922
+ if (error) {
923
+ throw error
924
+ }
859
925
 
860
- // Default to Authorization header if there is no existing session
861
- jwt = data.session?.access_token ?? undefined
862
- }
926
+ // Default to Authorization header if there is no existing session
927
+ jwt = data.session?.access_token ?? undefined
928
+ }
863
929
 
864
- return await _request(this.fetch, 'GET', `${this.url}/user`, {
865
- headers: this.headers,
866
- jwt: jwt,
867
- xform: _userResponse,
930
+ return await _request(this.fetch, 'GET', `${this.url}/user`, {
931
+ headers: this.headers,
932
+ jwt: jwt,
933
+ xform: _userResponse,
934
+ })
868
935
  })
869
936
  } catch (error) {
870
937
  if (isAuthError(error)) {
@@ -885,27 +952,29 @@ export default class GoTrueClient {
885
952
  } = {}
886
953
  ): Promise<UserResponse> {
887
954
  try {
888
- const { data: sessionData, error: sessionError } = await this.getSession()
889
- if (sessionError) {
890
- throw sessionError
891
- }
892
- if (!sessionData.session) {
893
- throw new AuthSessionMissingError()
894
- }
895
- const session: Session = sessionData.session
896
- const { data, error: userError } = await _request(this.fetch, 'PUT', `${this.url}/user`, {
897
- headers: this.headers,
898
- redirectTo: options?.emailRedirectTo,
899
- body: attributes,
900
- jwt: session.access_token,
901
- xform: _userResponse,
902
- })
903
- if (userError) throw userError
904
- session.user = data.user as User
905
- await this._saveSession(session)
906
- await this._notifyAllSubscribers('USER_UPDATED', session)
955
+ return await this._useSession(async (result) => {
956
+ const { data: sessionData, error: sessionError } = result
957
+ if (sessionError) {
958
+ throw sessionError
959
+ }
960
+ if (!sessionData.session) {
961
+ throw new AuthSessionMissingError()
962
+ }
963
+ const session: Session = sessionData.session
964
+ const { data, error: userError } = await _request(this.fetch, 'PUT', `${this.url}/user`, {
965
+ headers: this.headers,
966
+ redirectTo: options?.emailRedirectTo,
967
+ body: attributes,
968
+ jwt: session.access_token,
969
+ xform: _userResponse,
970
+ })
971
+ if (userError) throw userError
972
+ session.user = data.user as User
973
+ await this._saveSession(session)
974
+ await this._notifyAllSubscribers('USER_UPDATED', session)
907
975
 
908
- return { data: { user: session.user }, error: null }
976
+ return { data: { user: session.user }, error: null }
977
+ })
909
978
  } catch (error) {
910
979
  if (isAuthError(error)) {
911
980
  return { data: { user: null }, error }
@@ -997,29 +1066,31 @@ export default class GoTrueClient {
997
1066
  */
998
1067
  async refreshSession(currentSession?: { refresh_token: string }): Promise<AuthResponse> {
999
1068
  try {
1000
- if (!currentSession) {
1001
- const { data, error } = await this.getSession()
1002
- if (error) {
1003
- throw error
1004
- }
1069
+ return await this._useSession(async (result) => {
1070
+ if (!currentSession) {
1071
+ const { data, error } = result
1072
+ if (error) {
1073
+ throw error
1074
+ }
1005
1075
 
1006
- currentSession = data.session ?? undefined
1007
- }
1076
+ currentSession = data.session ?? undefined
1077
+ }
1008
1078
 
1009
- if (!currentSession?.refresh_token) {
1010
- throw new AuthSessionMissingError()
1011
- }
1079
+ if (!currentSession?.refresh_token) {
1080
+ throw new AuthSessionMissingError()
1081
+ }
1012
1082
 
1013
- const { session, error } = await this._callRefreshToken(currentSession.refresh_token)
1014
- if (error) {
1015
- return { data: { user: null, session: null }, error: error }
1016
- }
1083
+ const { session, error } = await this._callRefreshToken(currentSession.refresh_token)
1084
+ if (error) {
1085
+ return { data: { user: null, session: null }, error: error }
1086
+ }
1017
1087
 
1018
- if (!session) {
1019
- return { data: { user: null, session: null }, error: null }
1020
- }
1088
+ if (!session) {
1089
+ return { data: { user: null, session: null }, error: null }
1090
+ }
1021
1091
 
1022
- return { data: { user: session.user, session }, error: null }
1092
+ return { data: { user: session.user, session }, error: null }
1093
+ })
1023
1094
  } catch (error) {
1024
1095
  if (isAuthError(error)) {
1025
1096
  return { data: { user: null, session: null }, error }
@@ -1142,27 +1213,29 @@ export default class GoTrueClient {
1142
1213
  * If using others scope, no `SIGNED_OUT` event is fired!
1143
1214
  */
1144
1215
  async signOut({ scope }: SignOut = { scope: 'global' }): Promise<{ error: AuthError | null }> {
1145
- const { data, error: sessionError } = await this.getSession()
1146
- if (sessionError) {
1147
- return { error: sessionError }
1148
- }
1149
- const accessToken = data.session?.access_token
1150
- if (accessToken) {
1151
- const { error } = await this.admin.signOut(accessToken, scope)
1152
- if (error) {
1153
- // ignore 404s since user might not exist anymore
1154
- // ignore 401s since an invalid or expired JWT should sign out the current session
1155
- if (!(isAuthApiError(error) && (error.status === 404 || error.status === 401))) {
1156
- return { error }
1216
+ return await this._useSession(async (result) => {
1217
+ const { data, error: sessionError } = result
1218
+ if (sessionError) {
1219
+ return { error: sessionError }
1220
+ }
1221
+ const accessToken = data.session?.access_token
1222
+ if (accessToken) {
1223
+ const { error } = await this.admin.signOut(accessToken, scope)
1224
+ if (error) {
1225
+ // ignore 404s since user might not exist anymore
1226
+ // ignore 401s since an invalid or expired JWT should sign out the current session
1227
+ if (!(isAuthApiError(error) && (error.status === 404 || error.status === 401))) {
1228
+ return { error }
1229
+ }
1157
1230
  }
1158
1231
  }
1159
- }
1160
- if (scope !== 'others') {
1161
- await this._removeSession()
1162
- await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`)
1163
- await this._notifyAllSubscribers('SIGNED_OUT', null)
1164
- }
1165
- return { error: null }
1232
+ if (scope !== 'others') {
1233
+ await this._removeSession()
1234
+ await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`)
1235
+ await this._notifyAllSubscribers('SIGNED_OUT', null)
1236
+ }
1237
+ return { error: null }
1238
+ })
1166
1239
  }
1167
1240
 
1168
1241
  /**
@@ -1195,20 +1268,22 @@ export default class GoTrueClient {
1195
1268
  }
1196
1269
 
1197
1270
  private async _emitInitialSession(id: string): Promise<void> {
1198
- try {
1199
- const {
1200
- data: { session },
1201
- error,
1202
- } = await this.getSession()
1203
- if (error) throw error
1271
+ return await this._useSession(async (result) => {
1272
+ try {
1273
+ const {
1274
+ data: { session },
1275
+ error,
1276
+ } = result
1277
+ if (error) throw error
1204
1278
 
1205
- await this.stateChangeEmitters.get(id)?.callback('INITIAL_SESSION', session)
1206
- this._debug('INITIAL_SESSION', 'callback id', id, 'session', session)
1207
- } catch (err) {
1208
- await this.stateChangeEmitters.get(id)?.callback('INITIAL_SESSION', null)
1209
- this._debug('INITIAL_SESSION', 'callback id', id, 'error', err)
1210
- console.error(err)
1211
- }
1279
+ await this.stateChangeEmitters.get(id)?.callback('INITIAL_SESSION', session)
1280
+ this._debug('INITIAL_SESSION', 'callback id', id, 'session', session)
1281
+ } catch (err) {
1282
+ await this.stateChangeEmitters.get(id)?.callback('INITIAL_SESSION', null)
1283
+ this._debug('INITIAL_SESSION', 'callback id', id, 'error', err)
1284
+ console.error(err)
1285
+ }
1286
+ })
1212
1287
  }
1213
1288
 
1214
1289
  /**
@@ -1634,28 +1709,30 @@ export default class GoTrueClient {
1634
1709
  const now = Date.now()
1635
1710
 
1636
1711
  try {
1637
- const {
1638
- data: { session },
1639
- } = await this.getSession()
1640
-
1641
- if (!session || !session.refresh_token || !session.expires_at) {
1642
- this._debug('#_autoRefreshTokenTick()', 'no session')
1643
- return
1644
- }
1712
+ return await this._useSession(async (result) => {
1713
+ const {
1714
+ data: { session },
1715
+ } = result
1716
+
1717
+ if (!session || !session.refresh_token || !session.expires_at) {
1718
+ this._debug('#_autoRefreshTokenTick()', 'no session')
1719
+ return
1720
+ }
1645
1721
 
1646
- // session will expire in this many ticks (or has already expired if <= 0)
1647
- const expiresInTicks = Math.floor(
1648
- (session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION
1649
- )
1722
+ // session will expire in this many ticks (or has already expired if <= 0)
1723
+ const expiresInTicks = Math.floor(
1724
+ (session.expires_at * 1000 - now) / AUTO_REFRESH_TICK_DURATION
1725
+ )
1650
1726
 
1651
- this._debug(
1652
- '#_autoRefreshTokenTick()',
1653
- `access token expires in ${expiresInTicks} ticks, a tick lasts ${AUTO_REFRESH_TICK_DURATION}ms, refresh threshold is ${AUTO_REFRESH_TICK_THRESHOLD} ticks`
1654
- )
1727
+ this._debug(
1728
+ '#_autoRefreshTokenTick()',
1729
+ `access token expires in ${expiresInTicks} ticks, a tick lasts ${AUTO_REFRESH_TICK_DURATION}ms, refresh threshold is ${AUTO_REFRESH_TICK_THRESHOLD} ticks`
1730
+ )
1655
1731
 
1656
- if (expiresInTicks <= AUTO_REFRESH_TICK_THRESHOLD) {
1657
- await this._callRefreshToken(session.refresh_token)
1658
- }
1732
+ if (expiresInTicks <= AUTO_REFRESH_TICK_THRESHOLD) {
1733
+ await this._callRefreshToken(session.refresh_token)
1734
+ }
1735
+ })
1659
1736
  } catch (e: any) {
1660
1737
  console.error('Auto refresh tick failed with error. This is likely a transient error.', e)
1661
1738
  }
@@ -1777,14 +1854,16 @@ export default class GoTrueClient {
1777
1854
 
1778
1855
  private async _unenroll(params: MFAUnenrollParams): Promise<AuthMFAUnenrollResponse> {
1779
1856
  try {
1780
- const { data: sessionData, error: sessionError } = await this.getSession()
1781
- if (sessionError) {
1782
- return { data: null, error: sessionError }
1783
- }
1857
+ return await this._useSession(async (result) => {
1858
+ const { data: sessionData, error: sessionError } = result
1859
+ if (sessionError) {
1860
+ return { data: null, error: sessionError }
1861
+ }
1784
1862
 
1785
- return await _request(this.fetch, 'DELETE', `${this.url}/factors/${params.factorId}`, {
1786
- headers: this.headers,
1787
- jwt: sessionData?.session?.access_token,
1863
+ return await _request(this.fetch, 'DELETE', `${this.url}/factors/${params.factorId}`, {
1864
+ headers: this.headers,
1865
+ jwt: sessionData?.session?.access_token,
1866
+ })
1788
1867
  })
1789
1868
  } catch (error) {
1790
1869
  if (isAuthError(error)) {
@@ -1799,30 +1878,32 @@ export default class GoTrueClient {
1799
1878
  */
1800
1879
  private async _enroll(params: MFAEnrollParams): Promise<AuthMFAEnrollResponse> {
1801
1880
  try {
1802
- const { data: sessionData, error: sessionError } = await this.getSession()
1803
- if (sessionError) {
1804
- return { data: null, error: sessionError }
1805
- }
1881
+ return await this._useSession(async (result) => {
1882
+ const { data: sessionData, error: sessionError } = result
1883
+ if (sessionError) {
1884
+ return { data: null, error: sessionError }
1885
+ }
1806
1886
 
1807
- const { data, error } = await _request(this.fetch, 'POST', `${this.url}/factors`, {
1808
- body: {
1809
- friendly_name: params.friendlyName,
1810
- factor_type: params.factorType,
1811
- issuer: params.issuer,
1812
- },
1813
- headers: this.headers,
1814
- jwt: sessionData?.session?.access_token,
1815
- })
1887
+ const { data, error } = await _request(this.fetch, 'POST', `${this.url}/factors`, {
1888
+ body: {
1889
+ friendly_name: params.friendlyName,
1890
+ factor_type: params.factorType,
1891
+ issuer: params.issuer,
1892
+ },
1893
+ headers: this.headers,
1894
+ jwt: sessionData?.session?.access_token,
1895
+ })
1816
1896
 
1817
- if (error) {
1818
- return { data: null, error }
1819
- }
1897
+ if (error) {
1898
+ return { data: null, error }
1899
+ }
1820
1900
 
1821
- if (data?.totp?.qr_code) {
1822
- data.totp.qr_code = `data:image/svg+xml;utf-8,${data.totp.qr_code}`
1823
- }
1901
+ if (data?.totp?.qr_code) {
1902
+ data.totp.qr_code = `data:image/svg+xml;utf-8,${data.totp.qr_code}`
1903
+ }
1824
1904
 
1825
- return { data, error: null }
1905
+ return { data, error: null }
1906
+ })
1826
1907
  } catch (error) {
1827
1908
  if (isAuthError(error)) {
1828
1909
  return { data: null, error }
@@ -1836,32 +1917,34 @@ export default class GoTrueClient {
1836
1917
  */
1837
1918
  private async _verify(params: MFAVerifyParams): Promise<AuthMFAVerifyResponse> {
1838
1919
  try {
1839
- const { data: sessionData, error: sessionError } = await this.getSession()
1840
- if (sessionError) {
1841
- return { data: null, error: sessionError }
1842
- }
1920
+ return await this._useSession(async (result) => {
1921
+ const { data: sessionData, error: sessionError } = result
1922
+ if (sessionError) {
1923
+ return { data: null, error: sessionError }
1924
+ }
1843
1925
 
1844
- const { data, error } = await _request(
1845
- this.fetch,
1846
- 'POST',
1847
- `${this.url}/factors/${params.factorId}/verify`,
1848
- {
1849
- body: { code: params.code, challenge_id: params.challengeId },
1850
- headers: this.headers,
1851
- jwt: sessionData?.session?.access_token,
1926
+ const { data, error } = await _request(
1927
+ this.fetch,
1928
+ 'POST',
1929
+ `${this.url}/factors/${params.factorId}/verify`,
1930
+ {
1931
+ body: { code: params.code, challenge_id: params.challengeId },
1932
+ headers: this.headers,
1933
+ jwt: sessionData?.session?.access_token,
1934
+ }
1935
+ )
1936
+ if (error) {
1937
+ return { data: null, error }
1852
1938
  }
1853
- )
1854
- if (error) {
1855
- return { data: null, error }
1856
- }
1857
1939
 
1858
- await this._saveSession({
1859
- expires_at: Math.round(Date.now() / 1000) + data.expires_in,
1860
- ...data,
1861
- })
1862
- await this._notifyAllSubscribers('MFA_CHALLENGE_VERIFIED', data)
1940
+ await this._saveSession({
1941
+ expires_at: Math.round(Date.now() / 1000) + data.expires_in,
1942
+ ...data,
1943
+ })
1944
+ await this._notifyAllSubscribers('MFA_CHALLENGE_VERIFIED', data)
1863
1945
 
1864
- return { data, error }
1946
+ return { data, error }
1947
+ })
1865
1948
  } catch (error) {
1866
1949
  if (isAuthError(error)) {
1867
1950
  return { data: null, error }
@@ -1875,20 +1958,22 @@ export default class GoTrueClient {
1875
1958
  */
1876
1959
  private async _challenge(params: MFAChallengeParams): Promise<AuthMFAChallengeResponse> {
1877
1960
  try {
1878
- const { data: sessionData, error: sessionError } = await this.getSession()
1879
- if (sessionError) {
1880
- return { data: null, error: sessionError }
1881
- }
1882
-
1883
- return await _request(
1884
- this.fetch,
1885
- 'POST',
1886
- `${this.url}/factors/${params.factorId}/challenge`,
1887
- {
1888
- headers: this.headers,
1889
- jwt: sessionData?.session?.access_token,
1961
+ return await this._useSession(async (result) => {
1962
+ const { data: sessionData, error: sessionError } = result
1963
+ if (sessionError) {
1964
+ return { data: null, error: sessionError }
1890
1965
  }
1891
- )
1966
+
1967
+ return await _request(
1968
+ this.fetch,
1969
+ 'POST',
1970
+ `${this.url}/factors/${params.factorId}/challenge`,
1971
+ {
1972
+ headers: this.headers,
1973
+ jwt: sessionData?.session?.access_token,
1974
+ }
1975
+ )
1976
+ })
1892
1977
  } catch (error) {
1893
1978
  if (isAuthError(error)) {
1894
1979
  return { data: null, error }
@@ -1946,39 +2031,41 @@ export default class GoTrueClient {
1946
2031
  * {@see GoTrueMFAApi#getAuthenticatorAssuranceLevel}
1947
2032
  */
1948
2033
  private async _getAuthenticatorAssuranceLevel(): Promise<AuthMFAGetAuthenticatorAssuranceLevelResponse> {
1949
- const {
1950
- data: { session },
1951
- error: sessionError,
1952
- } = await this.getSession()
1953
- if (sessionError) {
1954
- return { data: null, error: sessionError }
1955
- }
1956
- if (!session) {
1957
- return {
1958
- data: { currentLevel: null, nextLevel: null, currentAuthenticationMethods: [] },
1959
- error: null,
2034
+ return await this._useSession(async (result) => {
2035
+ const {
2036
+ data: { session },
2037
+ error: sessionError,
2038
+ } = result
2039
+ if (sessionError) {
2040
+ return { data: null, error: sessionError }
2041
+ }
2042
+ if (!session) {
2043
+ return {
2044
+ data: { currentLevel: null, nextLevel: null, currentAuthenticationMethods: [] },
2045
+ error: null,
2046
+ }
1960
2047
  }
1961
- }
1962
2048
 
1963
- const payload = this._decodeJWT(session.access_token)
2049
+ const payload = this._decodeJWT(session.access_token)
1964
2050
 
1965
- let currentLevel: AuthenticatorAssuranceLevels | null = null
2051
+ let currentLevel: AuthenticatorAssuranceLevels | null = null
1966
2052
 
1967
- if (payload.aal) {
1968
- currentLevel = payload.aal
1969
- }
2053
+ if (payload.aal) {
2054
+ currentLevel = payload.aal
2055
+ }
1970
2056
 
1971
- let nextLevel: AuthenticatorAssuranceLevels | null = currentLevel
2057
+ let nextLevel: AuthenticatorAssuranceLevels | null = currentLevel
1972
2058
 
1973
- const verifiedFactors =
1974
- session.user.factors?.filter((factor: Factor) => factor.status === 'verified') ?? []
2059
+ const verifiedFactors =
2060
+ session.user.factors?.filter((factor: Factor) => factor.status === 'verified') ?? []
1975
2061
 
1976
- if (verifiedFactors.length > 0) {
1977
- nextLevel = 'aal2'
1978
- }
2062
+ if (verifiedFactors.length > 0) {
2063
+ nextLevel = 'aal2'
2064
+ }
1979
2065
 
1980
- const currentAuthenticationMethods = payload.amr || []
2066
+ const currentAuthenticationMethods = payload.amr || []
1981
2067
 
1982
- return { data: { currentLevel, nextLevel, currentAuthenticationMethods }, error: null }
2068
+ return { data: { currentLevel, nextLevel, currentAuthenticationMethods }, error: null }
2069
+ })
1983
2070
  }
1984
2071
  }