@supabase/gotrue-js 2.79.1-canary.2 → 2.80.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.
Files changed (64) hide show
  1. package/README.md +35 -17
  2. package/dist/main/AuthAdminApi.js +2 -4
  3. package/dist/main/AuthAdminApi.js.map +1 -1
  4. package/dist/main/AuthClient.js +2 -4
  5. package/dist/main/AuthClient.js.map +1 -1
  6. package/dist/main/GoTrueAdminApi.d.ts +7 -0
  7. package/dist/main/GoTrueAdminApi.d.ts.map +1 -1
  8. package/dist/main/GoTrueAdminApi.js +28 -15
  9. package/dist/main/GoTrueAdminApi.js.map +1 -1
  10. package/dist/main/GoTrueClient.d.ts +38 -1
  11. package/dist/main/GoTrueClient.d.ts.map +1 -1
  12. package/dist/main/GoTrueClient.js +293 -152
  13. package/dist/main/GoTrueClient.js.map +1 -1
  14. package/dist/main/index.js +7 -23
  15. package/dist/main/index.js.map +1 -1
  16. package/dist/main/lib/error-codes.d.ts +1 -1
  17. package/dist/main/lib/fetch.js +2 -12
  18. package/dist/main/lib/fetch.js.map +1 -1
  19. package/dist/main/lib/helpers.d.ts +11 -0
  20. package/dist/main/lib/helpers.d.ts.map +1 -1
  21. package/dist/main/lib/helpers.js +39 -42
  22. package/dist/main/lib/helpers.js.map +1 -1
  23. package/dist/main/lib/types.d.ts +145 -3
  24. package/dist/main/lib/types.d.ts.map +1 -1
  25. package/dist/main/lib/types.js.map +1 -1
  26. package/dist/main/lib/version.d.ts +1 -1
  27. package/dist/main/lib/version.d.ts.map +1 -1
  28. package/dist/main/lib/version.js +1 -1
  29. package/dist/main/lib/version.js.map +1 -1
  30. package/dist/main/lib/webauthn.js +3 -13
  31. package/dist/main/lib/webauthn.js.map +1 -1
  32. package/dist/module/GoTrueAdminApi.d.ts +7 -0
  33. package/dist/module/GoTrueAdminApi.d.ts.map +1 -1
  34. package/dist/module/GoTrueAdminApi.js +27 -14
  35. package/dist/module/GoTrueAdminApi.js.map +1 -1
  36. package/dist/module/GoTrueClient.d.ts +38 -1
  37. package/dist/module/GoTrueClient.d.ts.map +1 -1
  38. package/dist/module/GoTrueClient.js +292 -149
  39. package/dist/module/GoTrueClient.js.map +1 -1
  40. package/dist/module/lib/error-codes.d.ts +1 -1
  41. package/dist/module/lib/fetch.js +1 -11
  42. package/dist/module/lib/fetch.js.map +1 -1
  43. package/dist/module/lib/helpers.d.ts +11 -0
  44. package/dist/module/lib/helpers.d.ts.map +1 -1
  45. package/dist/module/lib/helpers.js +38 -9
  46. package/dist/module/lib/helpers.js.map +1 -1
  47. package/dist/module/lib/types.d.ts +145 -3
  48. package/dist/module/lib/types.d.ts.map +1 -1
  49. package/dist/module/lib/types.js.map +1 -1
  50. package/dist/module/lib/version.d.ts +1 -1
  51. package/dist/module/lib/version.d.ts.map +1 -1
  52. package/dist/module/lib/version.js +1 -1
  53. package/dist/module/lib/version.js.map +1 -1
  54. package/dist/module/lib/webauthn.js +1 -11
  55. package/dist/module/lib/webauthn.js.map +1 -1
  56. package/dist/tsconfig.module.tsbuildinfo +1 -0
  57. package/dist/tsconfig.tsbuildinfo +1 -0
  58. package/package.json +9 -10
  59. package/src/GoTrueAdminApi.ts +38 -15
  60. package/src/GoTrueClient.ts +356 -150
  61. package/src/lib/error-codes.ts +1 -1
  62. package/src/lib/helpers.ts +46 -8
  63. package/src/lib/types.ts +159 -2
  64. package/src/lib/version.ts +1 -1
@@ -38,6 +38,7 @@ import {
38
38
  getAlgorithm,
39
39
  getCodeChallengeAndMethod,
40
40
  getItemAsync,
41
+ insecureUserWarningProxy,
41
42
  isBrowser,
42
43
  parseParametersFromURL,
43
44
  removeItemAsync,
@@ -105,6 +106,9 @@ import type {
105
106
  MFAVerifyWebauthnParamFields,
106
107
  MFAVerifyWebauthnParams,
107
108
  OAuthResponse,
109
+ AuthOAuthServerApi,
110
+ AuthOAuthAuthorizationDetailsResponse,
111
+ AuthOAuthConsentResponse,
108
112
  Prettify,
109
113
  Provider,
110
114
  ResendParams,
@@ -166,6 +170,7 @@ const DEFAULT_OPTIONS: Omit<
166
170
  flowType: 'implicit',
167
171
  debug: false,
168
172
  hasCustomAuthorizationHeader: false,
173
+ throwOnError: false,
169
174
  }
170
175
 
171
176
  async function lockNoOp<R>(name: string, acquireTimeout: number, fn: () => Promise<R>): Promise<R> {
@@ -183,7 +188,7 @@ async function lockNoOp<R>(name: string, acquireTimeout: number, fn: () => Promi
183
188
  const GLOBAL_JWKS: { [storageKey: string]: { cachedAt: number; jwks: { keys: JWK[] } } } = {}
184
189
 
185
190
  export default class GoTrueClient {
186
- private static nextInstanceID = 0
191
+ private static nextInstanceID: Record<string, number> = {}
187
192
 
188
193
  private instanceID: number
189
194
 
@@ -196,6 +201,12 @@ export default class GoTrueClient {
196
201
  * Namespace for the MFA methods.
197
202
  */
198
203
  mfa: GoTrueMFAApi
204
+ /**
205
+ * Namespace for the OAuth 2.1 authorization server methods.
206
+ * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
207
+ * Used to implement the authorization code flow on the consent page.
208
+ */
209
+ oauth: AuthOAuthServerApi
199
210
  /**
200
211
  * The storage key used to identify the values saved in localStorage
201
212
  */
@@ -252,6 +263,7 @@ export default class GoTrueClient {
252
263
  protected lock: LockFunc
253
264
  protected lockAcquired = false
254
265
  protected pendingInLock: Promise<any>[] = []
266
+ protected throwOnError: boolean
255
267
 
256
268
  /**
257
269
  * Used to broadcast state change events to other tabs listening.
@@ -265,24 +277,26 @@ export default class GoTrueClient {
265
277
  * Create a new client for use in the browser.
266
278
  */
267
279
  constructor(options: GoTrueClientOptions) {
268
- this.instanceID = GoTrueClient.nextInstanceID
269
- GoTrueClient.nextInstanceID += 1
270
-
271
- if (this.instanceID > 0 && isBrowser()) {
272
- console.warn(
273
- 'Multiple GoTrueClient instances detected in the same browser context. It is not an error, but this should be avoided as it may produce undefined behavior when used concurrently under the same storage key.'
274
- )
275
- }
276
-
277
280
  const settings = { ...DEFAULT_OPTIONS, ...options }
281
+ this.storageKey = settings.storageKey
282
+
283
+ this.instanceID = GoTrueClient.nextInstanceID[this.storageKey] ?? 0
284
+ GoTrueClient.nextInstanceID[this.storageKey] = this.instanceID + 1
278
285
 
279
286
  this.logDebugMessages = !!settings.debug
280
287
  if (typeof settings.debug === 'function') {
281
288
  this.logger = settings.debug
282
289
  }
283
290
 
291
+ if (this.instanceID > 0 && isBrowser()) {
292
+ const message = `${this._logPrefix()} Multiple GoTrueClient instances detected in the same browser context. It is not an error, but this should be avoided as it may produce undefined behavior when used concurrently under the same storage key.`
293
+ console.warn(message)
294
+ if (this.logDebugMessages) {
295
+ console.trace(message)
296
+ }
297
+ }
298
+
284
299
  this.persistSession = settings.persistSession
285
- this.storageKey = settings.storageKey
286
300
  this.autoRefreshToken = settings.autoRefreshToken
287
301
  this.admin = new GoTrueAdminApi({
288
302
  url: settings.url,
@@ -297,6 +311,7 @@ export default class GoTrueClient {
297
311
  this.detectSessionInUrl = settings.detectSessionInUrl
298
312
  this.flowType = settings.flowType
299
313
  this.hasCustomAuthorizationHeader = settings.hasCustomAuthorizationHeader
314
+ this.throwOnError = settings.throwOnError
300
315
 
301
316
  if (settings.lock) {
302
317
  this.lock = settings.lock
@@ -322,6 +337,12 @@ export default class GoTrueClient {
322
337
  webauthn: new WebAuthnApi(this),
323
338
  }
324
339
 
340
+ this.oauth = {
341
+ getAuthorizationDetails: this._getAuthorizationDetails.bind(this),
342
+ approveAuthorization: this._approveAuthorization.bind(this),
343
+ denyAuthorization: this._denyAuthorization.bind(this),
344
+ }
345
+
325
346
  if (this.persistSession) {
326
347
  if (settings.storage) {
327
348
  this.storage = settings.storage
@@ -362,12 +383,35 @@ export default class GoTrueClient {
362
383
  this.initialize()
363
384
  }
364
385
 
386
+ /**
387
+ * Returns whether error throwing mode is enabled for this client.
388
+ */
389
+ public isThrowOnErrorEnabled(): boolean {
390
+ return this.throwOnError
391
+ }
392
+
393
+ /**
394
+ * Centralizes return handling with optional error throwing. When `throwOnError` is enabled
395
+ * and the provided result contains a non-nullish error, the error is thrown instead of
396
+ * being returned. This ensures consistent behavior across all public API methods.
397
+ */
398
+ private _returnResult<T extends { error: any }>(result: T): T {
399
+ if (this.throwOnError && result && result.error) {
400
+ throw result.error
401
+ }
402
+ return result
403
+ }
404
+
405
+ private _logPrefix(): string {
406
+ return (
407
+ 'GoTrueClient@' +
408
+ `${this.storageKey}:${this.instanceID} (${version}) ${new Date().toISOString()}`
409
+ )
410
+ }
411
+
365
412
  private _debug(...args: any[]): GoTrueClient {
366
413
  if (this.logDebugMessages) {
367
- this.logger(
368
- `GoTrueClient@${this.instanceID} (${version}) ${new Date().toISOString()}`,
369
- ...args
370
- )
414
+ this.logger(this._logPrefix(), ...args)
371
415
  }
372
416
 
373
417
  return this
@@ -400,12 +444,16 @@ export default class GoTrueClient {
400
444
  */
401
445
  private async _initialize(): Promise<InitializeResult> {
402
446
  try {
403
- const params = parseParametersFromURL(window.location.href)
447
+ let params: { [parameter: string]: string } = {}
404
448
  let callbackUrlType = 'none'
405
- if (this._isImplicitGrantCallback(params)) {
406
- callbackUrlType = 'implicit'
407
- } else if (await this._isPKCECallback(params)) {
408
- callbackUrlType = 'pkce'
449
+
450
+ if (isBrowser()) {
451
+ params = parseParametersFromURL(window.location.href)
452
+ if (this._isImplicitGrantCallback(params)) {
453
+ callbackUrlType = 'implicit'
454
+ } else if (await this._isPKCECallback(params)) {
455
+ callbackUrlType = 'pkce'
456
+ }
409
457
  }
410
458
 
411
459
  /**
@@ -464,12 +512,12 @@ export default class GoTrueClient {
464
512
  return { error: null }
465
513
  } catch (error) {
466
514
  if (isAuthError(error)) {
467
- return { error }
515
+ return this._returnResult({ error })
468
516
  }
469
517
 
470
- return {
518
+ return this._returnResult({
471
519
  error: new AuthUnknownError('Unexpected error during initialization', error),
472
- }
520
+ })
473
521
  } finally {
474
522
  await this._handleVisibilityChange()
475
523
  this._debug('#_initialize()', 'end')
@@ -494,7 +542,7 @@ export default class GoTrueClient {
494
542
  const { data, error } = res
495
543
 
496
544
  if (error || !data) {
497
- return { data: { user: null, session: null }, error: error }
545
+ return this._returnResult({ data: { user: null, session: null }, error: error })
498
546
  }
499
547
  const session: Session | null = data.session
500
548
  const user: User | null = data.user
@@ -504,10 +552,10 @@ export default class GoTrueClient {
504
552
  await this._notifyAllSubscribers('SIGNED_IN', session)
505
553
  }
506
554
 
507
- return { data: { user, session }, error: null }
555
+ return this._returnResult({ data: { user, session }, error: null })
508
556
  } catch (error) {
509
557
  if (isAuthError(error)) {
510
- return { data: { user: null, session: null }, error }
558
+ return this._returnResult({ data: { user: null, session: null }, error })
511
559
  }
512
560
 
513
561
  throw error
@@ -572,7 +620,7 @@ export default class GoTrueClient {
572
620
  const { data, error } = res
573
621
 
574
622
  if (error || !data) {
575
- return { data: { user: null, session: null }, error: error }
623
+ return this._returnResult({ data: { user: null, session: null }, error: error })
576
624
  }
577
625
 
578
626
  const session: Session | null = data.session
@@ -583,10 +631,10 @@ export default class GoTrueClient {
583
631
  await this._notifyAllSubscribers('SIGNED_IN', session)
584
632
  }
585
633
 
586
- return { data: { user, session }, error: null }
634
+ return this._returnResult({ data: { user, session }, error: null })
587
635
  } catch (error) {
588
636
  if (isAuthError(error)) {
589
- return { data: { user: null, session: null }, error }
637
+ return this._returnResult({ data: { user: null, session: null }, error })
590
638
  }
591
639
 
592
640
  throw error
@@ -636,25 +684,26 @@ export default class GoTrueClient {
636
684
  const { data, error } = res
637
685
 
638
686
  if (error) {
639
- return { data: { user: null, session: null }, error }
687
+ return this._returnResult({ data: { user: null, session: null }, error })
640
688
  } else if (!data || !data.session || !data.user) {
641
- return { data: { user: null, session: null }, error: new AuthInvalidTokenResponseError() }
689
+ const invalidTokenError = new AuthInvalidTokenResponseError()
690
+ return this._returnResult({ data: { user: null, session: null }, error: invalidTokenError })
642
691
  }
643
692
  if (data.session) {
644
693
  await this._saveSession(data.session)
645
694
  await this._notifyAllSubscribers('SIGNED_IN', data.session)
646
695
  }
647
- return {
696
+ return this._returnResult({
648
697
  data: {
649
698
  user: data.user,
650
699
  session: data.session,
651
700
  ...(data.weak_password ? { weakPassword: data.weak_password } : null),
652
701
  },
653
702
  error,
654
- }
703
+ })
655
704
  } catch (error) {
656
705
  if (isAuthError(error)) {
657
- return { data: { user: null, session: null }, error }
706
+ return this._returnResult({ data: { user: null, session: null }, error })
658
707
  }
659
708
  throw error
660
709
  }
@@ -830,19 +879,17 @@ export default class GoTrueClient {
830
879
  throw error
831
880
  }
832
881
  if (!data || !data.session || !data.user) {
833
- return {
834
- data: { user: null, session: null },
835
- error: new AuthInvalidTokenResponseError(),
836
- }
882
+ const invalidTokenError = new AuthInvalidTokenResponseError()
883
+ return this._returnResult({ data: { user: null, session: null }, error: invalidTokenError })
837
884
  }
838
885
  if (data.session) {
839
886
  await this._saveSession(data.session)
840
887
  await this._notifyAllSubscribers('SIGNED_IN', data.session)
841
888
  }
842
- return { data: { ...data }, error }
889
+ return this._returnResult({ data: { ...data }, error })
843
890
  } catch (error) {
844
891
  if (isAuthError(error)) {
845
- return { data: { user: null, session: null }, error }
892
+ return this._returnResult({ data: { user: null, session: null }, error })
846
893
  }
847
894
 
848
895
  throw error
@@ -1018,19 +1065,17 @@ export default class GoTrueClient {
1018
1065
  throw error
1019
1066
  }
1020
1067
  if (!data || !data.session || !data.user) {
1021
- return {
1022
- data: { user: null, session: null },
1023
- error: new AuthInvalidTokenResponseError(),
1024
- }
1068
+ const invalidTokenError = new AuthInvalidTokenResponseError()
1069
+ return this._returnResult({ data: { user: null, session: null }, error: invalidTokenError })
1025
1070
  }
1026
1071
  if (data.session) {
1027
1072
  await this._saveSession(data.session)
1028
1073
  await this._notifyAllSubscribers('SIGNED_IN', data.session)
1029
1074
  }
1030
- return { data: { ...data }, error }
1075
+ return this._returnResult({ data: { ...data }, error })
1031
1076
  } catch (error) {
1032
1077
  if (isAuthError(error)) {
1033
- return { data: { user: null, session: null }, error }
1078
+ return this._returnResult({ data: { user: null, session: null }, error })
1034
1079
  }
1035
1080
 
1036
1081
  throw error
@@ -1066,19 +1111,23 @@ export default class GoTrueClient {
1066
1111
  throw error
1067
1112
  }
1068
1113
  if (!data || !data.session || !data.user) {
1069
- return {
1114
+ const invalidTokenError = new AuthInvalidTokenResponseError()
1115
+ return this._returnResult({
1070
1116
  data: { user: null, session: null, redirectType: null },
1071
- error: new AuthInvalidTokenResponseError(),
1072
- }
1117
+ error: invalidTokenError,
1118
+ })
1073
1119
  }
1074
1120
  if (data.session) {
1075
1121
  await this._saveSession(data.session)
1076
1122
  await this._notifyAllSubscribers('SIGNED_IN', data.session)
1077
1123
  }
1078
- return { data: { ...data, redirectType: redirectType ?? null }, error }
1124
+ return this._returnResult({ data: { ...data, redirectType: redirectType ?? null }, error })
1079
1125
  } catch (error) {
1080
1126
  if (isAuthError(error)) {
1081
- return { data: { user: null, session: null, redirectType: null }, error }
1127
+ return this._returnResult({
1128
+ data: { user: null, session: null, redirectType: null },
1129
+ error,
1130
+ })
1082
1131
  }
1083
1132
 
1084
1133
  throw error
@@ -1107,21 +1156,19 @@ export default class GoTrueClient {
1107
1156
 
1108
1157
  const { data, error } = res
1109
1158
  if (error) {
1110
- return { data: { user: null, session: null }, error }
1159
+ return this._returnResult({ data: { user: null, session: null }, error })
1111
1160
  } else if (!data || !data.session || !data.user) {
1112
- return {
1113
- data: { user: null, session: null },
1114
- error: new AuthInvalidTokenResponseError(),
1115
- }
1161
+ const invalidTokenError = new AuthInvalidTokenResponseError()
1162
+ return this._returnResult({ data: { user: null, session: null }, error: invalidTokenError })
1116
1163
  }
1117
1164
  if (data.session) {
1118
1165
  await this._saveSession(data.session)
1119
1166
  await this._notifyAllSubscribers('SIGNED_IN', data.session)
1120
1167
  }
1121
- return { data, error }
1168
+ return this._returnResult({ data, error })
1122
1169
  } catch (error) {
1123
1170
  if (isAuthError(error)) {
1124
- return { data: { user: null, session: null }, error }
1171
+ return this._returnResult({ data: { user: null, session: null }, error })
1125
1172
  }
1126
1173
  throw error
1127
1174
  }
@@ -1168,7 +1215,7 @@ export default class GoTrueClient {
1168
1215
  },
1169
1216
  redirectTo: options?.emailRedirectTo,
1170
1217
  })
1171
- return { data: { user: null, session: null }, error }
1218
+ return this._returnResult({ data: { user: null, session: null }, error })
1172
1219
  }
1173
1220
  if ('phone' in credentials) {
1174
1221
  const { phone, options } = credentials
@@ -1182,12 +1229,15 @@ export default class GoTrueClient {
1182
1229
  channel: options?.channel ?? 'sms',
1183
1230
  },
1184
1231
  })
1185
- return { data: { user: null, session: null, messageId: data?.message_id }, error }
1232
+ return this._returnResult({
1233
+ data: { user: null, session: null, messageId: data?.message_id },
1234
+ error,
1235
+ })
1186
1236
  }
1187
1237
  throw new AuthInvalidCredentialsError('You must provide either an email or phone number.')
1188
1238
  } catch (error) {
1189
1239
  if (isAuthError(error)) {
1190
- return { data: { user: null, session: null }, error }
1240
+ return this._returnResult({ data: { user: null, session: null }, error })
1191
1241
  }
1192
1242
 
1193
1243
  throw error
@@ -1218,9 +1268,9 @@ export default class GoTrueClient {
1218
1268
  if (error) {
1219
1269
  throw error
1220
1270
  }
1221
-
1222
1271
  if (!data) {
1223
- throw new Error('An error occurred on token verification.')
1272
+ const tokenVerificationError = new Error('An error occurred on token verification.')
1273
+ throw tokenVerificationError
1224
1274
  }
1225
1275
 
1226
1276
  const session: Session | null = data.session
@@ -1234,10 +1284,10 @@ export default class GoTrueClient {
1234
1284
  )
1235
1285
  }
1236
1286
 
1237
- return { data: { user, session }, error: null }
1287
+ return this._returnResult({ data: { user, session }, error: null })
1238
1288
  } catch (error) {
1239
1289
  if (isAuthError(error)) {
1240
- return { data: { user: null, session: null }, error }
1290
+ return this._returnResult({ data: { user: null, session: null }, error })
1241
1291
  }
1242
1292
 
1243
1293
  throw error
@@ -1269,7 +1319,7 @@ export default class GoTrueClient {
1269
1319
  )
1270
1320
  }
1271
1321
 
1272
- return await _request(this.fetch, 'POST', `${this.url}/sso`, {
1322
+ const result = await _request(this.fetch, 'POST', `${this.url}/sso`, {
1273
1323
  body: {
1274
1324
  ...('providerId' in params ? { provider_id: params.providerId } : null),
1275
1325
  ...('domain' in params ? { domain: params.domain } : null),
@@ -1284,9 +1334,10 @@ export default class GoTrueClient {
1284
1334
  headers: this.headers,
1285
1335
  xform: _ssoResponse,
1286
1336
  })
1337
+ return this._returnResult(result)
1287
1338
  } catch (error) {
1288
1339
  if (isAuthError(error)) {
1289
- return { data: null, error }
1340
+ return this._returnResult({ data: null, error })
1290
1341
  }
1291
1342
  throw error
1292
1343
  }
@@ -1318,11 +1369,11 @@ export default class GoTrueClient {
1318
1369
  headers: this.headers,
1319
1370
  jwt: session.access_token,
1320
1371
  })
1321
- return { data: { user: null, session: null }, error }
1372
+ return this._returnResult({ data: { user: null, session: null }, error })
1322
1373
  })
1323
1374
  } catch (error) {
1324
1375
  if (isAuthError(error)) {
1325
- return { data: { user: null, session: null }, error }
1376
+ return this._returnResult({ data: { user: null, session: null }, error })
1326
1377
  }
1327
1378
  throw error
1328
1379
  }
@@ -1345,7 +1396,7 @@ export default class GoTrueClient {
1345
1396
  },
1346
1397
  redirectTo: options?.emailRedirectTo,
1347
1398
  })
1348
- return { data: { user: null, session: null }, error }
1399
+ return this._returnResult({ data: { user: null, session: null }, error })
1349
1400
  } else if ('phone' in credentials) {
1350
1401
  const { phone, type, options } = credentials
1351
1402
  const { data, error } = await _request(this.fetch, 'POST', endpoint, {
@@ -1356,14 +1407,17 @@ export default class GoTrueClient {
1356
1407
  gotrue_meta_security: { captcha_token: options?.captchaToken },
1357
1408
  },
1358
1409
  })
1359
- return { data: { user: null, session: null, messageId: data?.message_id }, error }
1410
+ return this._returnResult({
1411
+ data: { user: null, session: null, messageId: data?.message_id },
1412
+ error,
1413
+ })
1360
1414
  }
1361
1415
  throw new AuthInvalidCredentialsError(
1362
1416
  'You must provide either an email or phone number and a type'
1363
1417
  )
1364
1418
  } catch (error) {
1365
1419
  if (isAuthError(error)) {
1366
- return { data: { user: null, session: null }, error }
1420
+ return this._returnResult({ data: { user: null, session: null }, error })
1367
1421
  }
1368
1422
  throw error
1369
1423
  }
@@ -1585,22 +1639,20 @@ export default class GoTrueClient {
1585
1639
  }
1586
1640
  }
1587
1641
 
1588
- if (this.storage.isServer && currentSession.user) {
1589
- let suppressWarning = this.suppressGetSessionWarning
1590
- const proxySession: Session = new Proxy(currentSession, {
1591
- get: (target: any, prop: string, receiver: any) => {
1592
- if (!suppressWarning && prop === 'user') {
1593
- // only show warning when the user object is being accessed from the server
1594
- console.warn(
1595
- 'Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.'
1596
- )
1597
- suppressWarning = true // keeps this proxy instance from logging additional warnings
1598
- this.suppressGetSessionWarning = true // keeps this client's future proxy instances from warning
1599
- }
1600
- return Reflect.get(target, prop, receiver)
1601
- },
1602
- })
1603
- currentSession = proxySession
1642
+ // Wrap the user object with a warning proxy on the server
1643
+ // This warns when properties of the user are accessed, not when session.user itself is accessed
1644
+ if (
1645
+ this.storage.isServer &&
1646
+ currentSession.user &&
1647
+ !(currentSession.user as any).__isUserNotAvailableProxy
1648
+ ) {
1649
+ const suppressWarningRef = { value: this.suppressGetSessionWarning }
1650
+ currentSession.user = insecureUserWarningProxy(currentSession.user, suppressWarningRef)
1651
+
1652
+ // Update the client-level suppression flag when the proxy suppresses the warning
1653
+ if (suppressWarningRef.value) {
1654
+ this.suppressGetSessionWarning = true
1655
+ }
1604
1656
  }
1605
1657
 
1606
1658
  return { data: { session: currentSession }, error: null }
@@ -1608,10 +1660,10 @@ export default class GoTrueClient {
1608
1660
 
1609
1661
  const { data: session, error } = await this._callRefreshToken(currentSession.refresh_token)
1610
1662
  if (error) {
1611
- return { data: { session: null }, error }
1663
+ return this._returnResult({ data: { session: null }, error })
1612
1664
  }
1613
1665
 
1614
- return { data: { session }, error: null }
1666
+ return this._returnResult({ data: { session }, error: null })
1615
1667
  } finally {
1616
1668
  this._debug('#__loadSession()', 'end')
1617
1669
  }
@@ -1675,7 +1727,7 @@ export default class GoTrueClient {
1675
1727
  await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`)
1676
1728
  }
1677
1729
 
1678
- return { data: { user: null }, error }
1730
+ return this._returnResult({ data: { user: null }, error })
1679
1731
  }
1680
1732
 
1681
1733
  throw error
@@ -1734,15 +1786,17 @@ export default class GoTrueClient {
1734
1786
  jwt: session.access_token,
1735
1787
  xform: _userResponse,
1736
1788
  })
1737
- if (userError) throw userError
1789
+ if (userError) {
1790
+ throw userError
1791
+ }
1738
1792
  session.user = data.user as User
1739
1793
  await this._saveSession(session)
1740
1794
  await this._notifyAllSubscribers('USER_UPDATED', session)
1741
- return { data: { user: session.user }, error: null }
1795
+ return this._returnResult({ data: { user: session.user }, error: null })
1742
1796
  })
1743
1797
  } catch (error) {
1744
1798
  if (isAuthError(error)) {
1745
- return { data: { user: null }, error }
1799
+ return this._returnResult({ data: { user: null }, error })
1746
1800
  }
1747
1801
 
1748
1802
  throw error
@@ -1789,7 +1843,7 @@ export default class GoTrueClient {
1789
1843
  currentSession.refresh_token
1790
1844
  )
1791
1845
  if (error) {
1792
- return { data: { user: null, session: null }, error: error }
1846
+ return this._returnResult({ data: { user: null, session: null }, error: error })
1793
1847
  }
1794
1848
 
1795
1849
  if (!refreshedSession) {
@@ -1813,10 +1867,10 @@ export default class GoTrueClient {
1813
1867
  await this._notifyAllSubscribers('SIGNED_IN', session)
1814
1868
  }
1815
1869
 
1816
- return { data: { user: session.user, session }, error: null }
1870
+ return this._returnResult({ data: { user: session.user, session }, error: null })
1817
1871
  } catch (error) {
1818
1872
  if (isAuthError(error)) {
1819
- return { data: { session: null, user: null }, error }
1873
+ return this._returnResult({ data: { session: null, user: null }, error })
1820
1874
  }
1821
1875
 
1822
1876
  throw error
@@ -1857,18 +1911,18 @@ export default class GoTrueClient {
1857
1911
 
1858
1912
  const { data: session, error } = await this._callRefreshToken(currentSession.refresh_token)
1859
1913
  if (error) {
1860
- return { data: { user: null, session: null }, error: error }
1914
+ return this._returnResult({ data: { user: null, session: null }, error: error })
1861
1915
  }
1862
1916
 
1863
1917
  if (!session) {
1864
- return { data: { user: null, session: null }, error: null }
1918
+ return this._returnResult({ data: { user: null, session: null }, error: null })
1865
1919
  }
1866
1920
 
1867
- return { data: { user: session.user, session }, error: null }
1921
+ return this._returnResult({ data: { user: session.user, session }, error: null })
1868
1922
  })
1869
1923
  } catch (error) {
1870
1924
  if (isAuthError(error)) {
1871
- return { data: { user: null, session: null }, error }
1925
+ return this._returnResult({ data: { user: null, session: null }, error })
1872
1926
  }
1873
1927
 
1874
1928
  throw error
@@ -1999,10 +2053,10 @@ export default class GoTrueClient {
1999
2053
  window.location.hash = ''
2000
2054
  this._debug('#_getSessionFromURL()', 'clearing window.location.hash')
2001
2055
 
2002
- return { data: { session, redirectType: params.type }, error: null }
2056
+ return this._returnResult({ data: { session, redirectType: params.type }, error: null })
2003
2057
  } catch (error) {
2004
2058
  if (isAuthError(error)) {
2005
- return { data: { session: null, redirectType: null }, error }
2059
+ return this._returnResult({ data: { session: null, redirectType: null }, error })
2006
2060
  }
2007
2061
 
2008
2062
  throw error
@@ -2050,7 +2104,7 @@ export default class GoTrueClient {
2050
2104
  return await this._useSession(async (result) => {
2051
2105
  const { data, error: sessionError } = result
2052
2106
  if (sessionError) {
2053
- return { error: sessionError }
2107
+ return this._returnResult({ error: sessionError })
2054
2108
  }
2055
2109
  const accessToken = data.session?.access_token
2056
2110
  if (accessToken) {
@@ -2064,7 +2118,7 @@ export default class GoTrueClient {
2064
2118
  (error.status === 404 || error.status === 401 || error.status === 403)
2065
2119
  )
2066
2120
  ) {
2067
- return { error }
2121
+ return this._returnResult({ error })
2068
2122
  }
2069
2123
  }
2070
2124
  }
@@ -2072,7 +2126,7 @@ export default class GoTrueClient {
2072
2126
  await this._removeSession()
2073
2127
  await removeItemAsync(this.storage, `${this.storageKey}-code-verifier`)
2074
2128
  }
2075
- return { error: null }
2129
+ return this._returnResult({ error: null })
2076
2130
  })
2077
2131
  }
2078
2132
 
@@ -2194,7 +2248,7 @@ export default class GoTrueClient {
2194
2248
  })
2195
2249
  } catch (error) {
2196
2250
  if (isAuthError(error)) {
2197
- return { data: null, error }
2251
+ return this._returnResult({ data: null, error })
2198
2252
  }
2199
2253
 
2200
2254
  throw error
@@ -2216,10 +2270,10 @@ export default class GoTrueClient {
2216
2270
  try {
2217
2271
  const { data, error } = await this.getUser()
2218
2272
  if (error) throw error
2219
- return { data: { identities: data.user.identities ?? [] }, error: null }
2273
+ return this._returnResult({ data: { identities: data.user.identities ?? [] }, error: null })
2220
2274
  } catch (error) {
2221
2275
  if (isAuthError(error)) {
2222
- return { data: null, error }
2276
+ return this._returnResult({ data: null, error })
2223
2277
  }
2224
2278
  throw error
2225
2279
  }
@@ -2268,10 +2322,13 @@ export default class GoTrueClient {
2268
2322
  if (isBrowser() && !credentials.options?.skipBrowserRedirect) {
2269
2323
  window.location.assign(data?.url)
2270
2324
  }
2271
- return { data: { provider: credentials.provider, url: data?.url }, error: null }
2325
+ return this._returnResult({
2326
+ data: { provider: credentials.provider, url: data?.url },
2327
+ error: null,
2328
+ })
2272
2329
  } catch (error) {
2273
2330
  if (isAuthError(error)) {
2274
- return { data: { provider: credentials.provider, url: null }, error }
2331
+ return this._returnResult({ data: { provider: credentials.provider, url: null }, error })
2275
2332
  }
2276
2333
  throw error
2277
2334
  }
@@ -2306,21 +2363,21 @@ export default class GoTrueClient {
2306
2363
 
2307
2364
  const { data, error } = res
2308
2365
  if (error) {
2309
- return { data: { user: null, session: null }, error }
2366
+ return this._returnResult({ data: { user: null, session: null }, error })
2310
2367
  } else if (!data || !data.session || !data.user) {
2311
- return {
2368
+ return this._returnResult({
2312
2369
  data: { user: null, session: null },
2313
2370
  error: new AuthInvalidTokenResponseError(),
2314
- }
2371
+ })
2315
2372
  }
2316
2373
  if (data.session) {
2317
2374
  await this._saveSession(data.session)
2318
2375
  await this._notifyAllSubscribers('USER_UPDATED', data.session)
2319
2376
  }
2320
- return { data, error }
2377
+ return this._returnResult({ data, error })
2321
2378
  } catch (error) {
2322
2379
  if (isAuthError(error)) {
2323
- return { data: { user: null, session: null }, error }
2380
+ return this._returnResult({ data: { user: null, session: null }, error })
2324
2381
  }
2325
2382
  throw error
2326
2383
  }
@@ -2355,7 +2412,7 @@ export default class GoTrueClient {
2355
2412
  })
2356
2413
  } catch (error) {
2357
2414
  if (isAuthError(error)) {
2358
- return { data: null, error }
2415
+ return this._returnResult({ data: null, error })
2359
2416
  }
2360
2417
  throw error
2361
2418
  }
@@ -2401,7 +2458,7 @@ export default class GoTrueClient {
2401
2458
  this._debug(debugName, 'error', error)
2402
2459
 
2403
2460
  if (isAuthError(error)) {
2404
- return { data: { session: null, user: null }, error }
2461
+ return this._returnResult({ data: { session: null, user: null }, error })
2405
2462
  }
2406
2463
  throw error
2407
2464
  } finally {
@@ -3008,7 +3065,7 @@ export default class GoTrueClient {
3008
3065
  return await this._useSession(async (result) => {
3009
3066
  const { data: sessionData, error: sessionError } = result
3010
3067
  if (sessionError) {
3011
- return { data: null, error: sessionError }
3068
+ return this._returnResult({ data: null, error: sessionError })
3012
3069
  }
3013
3070
 
3014
3071
  return await _request(this.fetch, 'DELETE', `${this.url}/factors/${params.factorId}`, {
@@ -3018,7 +3075,7 @@ export default class GoTrueClient {
3018
3075
  })
3019
3076
  } catch (error) {
3020
3077
  if (isAuthError(error)) {
3021
- return { data: null, error }
3078
+ return this._returnResult({ data: null, error })
3022
3079
  }
3023
3080
  throw error
3024
3081
  }
@@ -3035,7 +3092,7 @@ export default class GoTrueClient {
3035
3092
  return await this._useSession(async (result) => {
3036
3093
  const { data: sessionData, error: sessionError } = result
3037
3094
  if (sessionError) {
3038
- return { data: null, error: sessionError }
3095
+ return this._returnResult({ data: null, error: sessionError })
3039
3096
  }
3040
3097
 
3041
3098
  const body = {
@@ -3054,18 +3111,18 @@ export default class GoTrueClient {
3054
3111
  jwt: sessionData?.session?.access_token,
3055
3112
  })) as AuthMFAEnrollResponse
3056
3113
  if (error) {
3057
- return { data: null, error }
3114
+ return this._returnResult({ data: null, error })
3058
3115
  }
3059
3116
 
3060
3117
  if (params.factorType === 'totp' && data.type === 'totp' && data?.totp?.qr_code) {
3061
3118
  data.totp.qr_code = `data:image/svg+xml;utf-8,${data.totp.qr_code}`
3062
3119
  }
3063
3120
 
3064
- return { data, error: null }
3121
+ return this._returnResult({ data, error: null })
3065
3122
  })
3066
3123
  } catch (error) {
3067
3124
  if (isAuthError(error)) {
3068
- return { data: null, error }
3125
+ return this._returnResult({ data: null, error })
3069
3126
  }
3070
3127
  throw error
3071
3128
  }
@@ -3085,7 +3142,7 @@ export default class GoTrueClient {
3085
3142
  return await this._useSession(async (result) => {
3086
3143
  const { data: sessionData, error: sessionError } = result
3087
3144
  if (sessionError) {
3088
- return { data: null, error: sessionError }
3145
+ return this._returnResult({ data: null, error: sessionError })
3089
3146
  }
3090
3147
 
3091
3148
  const body: StrictOmit<
@@ -3134,7 +3191,7 @@ export default class GoTrueClient {
3134
3191
  }
3135
3192
  )
3136
3193
  if (error) {
3137
- return { data: null, error }
3194
+ return this._returnResult({ data: null, error })
3138
3195
  }
3139
3196
 
3140
3197
  await this._saveSession({
@@ -3143,11 +3200,11 @@ export default class GoTrueClient {
3143
3200
  })
3144
3201
  await this._notifyAllSubscribers('MFA_CHALLENGE_VERIFIED', data)
3145
3202
 
3146
- return { data, error }
3203
+ return this._returnResult({ data, error })
3147
3204
  })
3148
3205
  } catch (error) {
3149
3206
  if (isAuthError(error)) {
3150
- return { data: null, error }
3207
+ return this._returnResult({ data: null, error })
3151
3208
  }
3152
3209
  throw error
3153
3210
  }
@@ -3172,7 +3229,7 @@ export default class GoTrueClient {
3172
3229
  return await this._useSession(async (result) => {
3173
3230
  const { data: sessionData, error: sessionError } = result
3174
3231
  if (sessionError) {
3175
- return { data: null, error: sessionError }
3232
+ return this._returnResult({ data: null, error: sessionError })
3176
3233
  }
3177
3234
 
3178
3235
  const response = (await _request(
@@ -3236,7 +3293,7 @@ export default class GoTrueClient {
3236
3293
  })
3237
3294
  } catch (error) {
3238
3295
  if (isAuthError(error)) {
3239
- return { data: null, error }
3296
+ return this._returnResult({ data: null, error })
3240
3297
  }
3241
3298
  throw error
3242
3299
  }
@@ -3256,7 +3313,7 @@ export default class GoTrueClient {
3256
3313
  factorId: params.factorId,
3257
3314
  })
3258
3315
  if (challengeError) {
3259
- return { data: null, error: challengeError }
3316
+ return this._returnResult({ data: null, error: challengeError })
3260
3317
  }
3261
3318
 
3262
3319
  return await this._verify({
@@ -3304,44 +3361,193 @@ export default class GoTrueClient {
3304
3361
  * {@see GoTrueMFAApi#getAuthenticatorAssuranceLevel}
3305
3362
  */
3306
3363
  private async _getAuthenticatorAssuranceLevel(): Promise<AuthMFAGetAuthenticatorAssuranceLevelResponse> {
3307
- return this._acquireLock(-1, async () => {
3364
+ const {
3365
+ data: { session },
3366
+ error: sessionError,
3367
+ } = await this.getSession()
3368
+
3369
+ if (sessionError) {
3370
+ return this._returnResult({ data: null, error: sessionError })
3371
+ }
3372
+ if (!session) {
3373
+ return {
3374
+ data: { currentLevel: null, nextLevel: null, currentAuthenticationMethods: [] },
3375
+ error: null,
3376
+ }
3377
+ }
3378
+
3379
+ const { payload } = decodeJWT(session.access_token)
3380
+
3381
+ let currentLevel: AuthenticatorAssuranceLevels | null = null
3382
+
3383
+ if (payload.aal) {
3384
+ currentLevel = payload.aal
3385
+ }
3386
+
3387
+ let nextLevel: AuthenticatorAssuranceLevels | null = currentLevel
3388
+
3389
+ const verifiedFactors =
3390
+ session.user.factors?.filter((factor: Factor) => factor.status === 'verified') ?? []
3391
+
3392
+ if (verifiedFactors.length > 0) {
3393
+ nextLevel = 'aal2'
3394
+ }
3395
+
3396
+ const currentAuthenticationMethods = payload.amr || []
3397
+
3398
+ return { data: { currentLevel, nextLevel, currentAuthenticationMethods }, error: null }
3399
+ }
3400
+
3401
+ /**
3402
+ * Retrieves details about an OAuth authorization request.
3403
+ * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
3404
+ *
3405
+ * Returns authorization details including client info, scopes, and user information.
3406
+ * If the API returns a redirect_uri, it means consent was already given - the caller
3407
+ * should handle the redirect manually if needed.
3408
+ */
3409
+ private async _getAuthorizationDetails(
3410
+ authorizationId: string
3411
+ ): Promise<AuthOAuthAuthorizationDetailsResponse> {
3412
+ try {
3308
3413
  return await this._useSession(async (result) => {
3309
3414
  const {
3310
3415
  data: { session },
3311
3416
  error: sessionError,
3312
3417
  } = result
3418
+
3313
3419
  if (sessionError) {
3314
- return { data: null, error: sessionError }
3420
+ return this._returnResult({ data: null, error: sessionError })
3315
3421
  }
3422
+
3316
3423
  if (!session) {
3317
- return {
3318
- data: { currentLevel: null, nextLevel: null, currentAuthenticationMethods: [] },
3319
- error: null,
3424
+ return this._returnResult({ data: null, error: new AuthSessionMissingError() })
3425
+ }
3426
+
3427
+ return await _request(
3428
+ this.fetch,
3429
+ 'GET',
3430
+ `${this.url}/oauth/authorizations/${authorizationId}`,
3431
+ {
3432
+ headers: this.headers,
3433
+ jwt: session.access_token,
3434
+ xform: (data: any) => ({ data, error: null }),
3320
3435
  }
3436
+ )
3437
+ })
3438
+ } catch (error) {
3439
+ if (isAuthError(error)) {
3440
+ return this._returnResult({ data: null, error })
3441
+ }
3442
+
3443
+ throw error
3444
+ }
3445
+ }
3446
+
3447
+ /**
3448
+ * Approves an OAuth authorization request.
3449
+ * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
3450
+ */
3451
+ private async _approveAuthorization(
3452
+ authorizationId: string,
3453
+ options?: { skipBrowserRedirect?: boolean }
3454
+ ): Promise<AuthOAuthConsentResponse> {
3455
+ try {
3456
+ return await this._useSession(async (result) => {
3457
+ const {
3458
+ data: { session },
3459
+ error: sessionError,
3460
+ } = result
3461
+
3462
+ if (sessionError) {
3463
+ return this._returnResult({ data: null, error: sessionError })
3321
3464
  }
3322
3465
 
3323
- const { payload } = decodeJWT(session.access_token)
3466
+ if (!session) {
3467
+ return this._returnResult({ data: null, error: new AuthSessionMissingError() })
3468
+ }
3324
3469
 
3325
- let currentLevel: AuthenticatorAssuranceLevels | null = null
3470
+ const response = await _request(
3471
+ this.fetch,
3472
+ 'POST',
3473
+ `${this.url}/oauth/authorizations/${authorizationId}/consent`,
3474
+ {
3475
+ headers: this.headers,
3476
+ jwt: session.access_token,
3477
+ body: { action: 'approve' },
3478
+ xform: (data: any) => ({ data, error: null }),
3479
+ }
3480
+ )
3326
3481
 
3327
- if (payload.aal) {
3328
- currentLevel = payload.aal
3482
+ if (response.data && response.data.redirect_url) {
3483
+ // Automatically redirect in browser unless skipBrowserRedirect is true
3484
+ if (isBrowser() && !options?.skipBrowserRedirect) {
3485
+ window.location.assign(response.data.redirect_url)
3486
+ }
3329
3487
  }
3330
3488
 
3331
- let nextLevel: AuthenticatorAssuranceLevels | null = currentLevel
3489
+ return response
3490
+ })
3491
+ } catch (error) {
3492
+ if (isAuthError(error)) {
3493
+ return this._returnResult({ data: null, error })
3494
+ }
3495
+
3496
+ throw error
3497
+ }
3498
+ }
3499
+
3500
+ /**
3501
+ * Denies an OAuth authorization request.
3502
+ * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
3503
+ */
3504
+ private async _denyAuthorization(
3505
+ authorizationId: string,
3506
+ options?: { skipBrowserRedirect?: boolean }
3507
+ ): Promise<AuthOAuthConsentResponse> {
3508
+ try {
3509
+ return await this._useSession(async (result) => {
3510
+ const {
3511
+ data: { session },
3512
+ error: sessionError,
3513
+ } = result
3332
3514
 
3333
- const verifiedFactors =
3334
- session.user.factors?.filter((factor: Factor) => factor.status === 'verified') ?? []
3515
+ if (sessionError) {
3516
+ return this._returnResult({ data: null, error: sessionError })
3517
+ }
3335
3518
 
3336
- if (verifiedFactors.length > 0) {
3337
- nextLevel = 'aal2'
3519
+ if (!session) {
3520
+ return this._returnResult({ data: null, error: new AuthSessionMissingError() })
3338
3521
  }
3339
3522
 
3340
- const currentAuthenticationMethods = payload.amr || []
3523
+ const response = await _request(
3524
+ this.fetch,
3525
+ 'POST',
3526
+ `${this.url}/oauth/authorizations/${authorizationId}/consent`,
3527
+ {
3528
+ headers: this.headers,
3529
+ jwt: session.access_token,
3530
+ body: { action: 'deny' },
3531
+ xform: (data: any) => ({ data, error: null }),
3532
+ }
3533
+ )
3534
+
3535
+ if (response.data && response.data.redirect_url) {
3536
+ // Automatically redirect in browser unless skipBrowserRedirect is true
3537
+ if (isBrowser() && !options?.skipBrowserRedirect) {
3538
+ window.location.assign(response.data.redirect_url)
3539
+ }
3540
+ }
3341
3541
 
3342
- return { data: { currentLevel, nextLevel, currentAuthenticationMethods }, error: null }
3542
+ return response
3343
3543
  })
3344
- })
3544
+ } catch (error) {
3545
+ if (isAuthError(error)) {
3546
+ return this._returnResult({ data: null, error })
3547
+ }
3548
+
3549
+ throw error
3550
+ }
3345
3551
  }
3346
3552
 
3347
3553
  private async fetchJwk(kid: string, jwks: { keys: JWK[] } = { keys: [] }): Promise<JWK | null> {
@@ -3425,7 +3631,7 @@ export default class GoTrueClient {
3425
3631
  if (!token) {
3426
3632
  const { data, error } = await this.getSession()
3427
3633
  if (error || !data.session) {
3428
- return { data: null, error }
3634
+ return this._returnResult({ data: null, error })
3429
3635
  }
3430
3636
  token = data.session.access_token
3431
3637
  }
@@ -3497,7 +3703,7 @@ export default class GoTrueClient {
3497
3703
  }
3498
3704
  } catch (error) {
3499
3705
  if (isAuthError(error)) {
3500
- return { data: null, error }
3706
+ return this._returnResult({ data: null, error })
3501
3707
  }
3502
3708
  throw error
3503
3709
  }