@depup/supabase__auth-js 2.110.8-depup.0 → 2.111.0-depup.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 (44) hide show
  1. package/README.md +2 -2
  2. package/changes.json +1 -1
  3. package/dist/main/GoTrueClient.d.ts +35 -2
  4. package/dist/main/GoTrueClient.d.ts.map +1 -1
  5. package/dist/main/GoTrueClient.js +139 -51
  6. package/dist/main/GoTrueClient.js.map +1 -1
  7. package/dist/main/lib/constants.d.ts +13 -0
  8. package/dist/main/lib/constants.d.ts.map +1 -1
  9. package/dist/main/lib/constants.js +14 -1
  10. package/dist/main/lib/constants.js.map +1 -1
  11. package/dist/main/lib/helpers.d.ts +50 -1
  12. package/dist/main/lib/helpers.d.ts.map +1 -1
  13. package/dist/main/lib/helpers.js +160 -4
  14. package/dist/main/lib/helpers.js.map +1 -1
  15. package/dist/main/lib/types.d.ts +38 -0
  16. package/dist/main/lib/types.d.ts.map +1 -1
  17. package/dist/main/lib/types.js.map +1 -1
  18. package/dist/main/lib/version.d.ts +1 -1
  19. package/dist/main/lib/version.js +1 -1
  20. package/dist/module/GoTrueClient.d.ts +35 -2
  21. package/dist/module/GoTrueClient.d.ts.map +1 -1
  22. package/dist/module/GoTrueClient.js +141 -53
  23. package/dist/module/GoTrueClient.js.map +1 -1
  24. package/dist/module/lib/constants.d.ts +13 -0
  25. package/dist/module/lib/constants.d.ts.map +1 -1
  26. package/dist/module/lib/constants.js +13 -0
  27. package/dist/module/lib/constants.js.map +1 -1
  28. package/dist/module/lib/helpers.d.ts +50 -1
  29. package/dist/module/lib/helpers.d.ts.map +1 -1
  30. package/dist/module/lib/helpers.js +152 -4
  31. package/dist/module/lib/helpers.js.map +1 -1
  32. package/dist/module/lib/types.d.ts +38 -0
  33. package/dist/module/lib/types.d.ts.map +1 -1
  34. package/dist/module/lib/types.js.map +1 -1
  35. package/dist/module/lib/version.d.ts +1 -1
  36. package/dist/module/lib/version.js +1 -1
  37. package/dist/tsconfig.module.tsbuildinfo +1 -1
  38. package/dist/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +3 -3
  40. package/src/GoTrueClient.ts +181 -71
  41. package/src/lib/constants.ts +15 -0
  42. package/src/lib/helpers.ts +197 -5
  43. package/src/lib/types.ts +38 -0
  44. package/src/lib/version.ts +1 -1
@@ -717,6 +717,7 @@ class GoTrueClient {
717
717
  */
718
718
  async signUp(credentials) {
719
719
  var _a, _b, _c;
720
+ let flowId = null;
720
721
  try {
721
722
  let res;
722
723
  if ('email' in credentials) {
@@ -725,11 +726,11 @@ class GoTrueClient {
725
726
  let codeChallengeMethod = null;
726
727
  if (this.flowType === 'pkce') {
727
728
  ;
728
- [codeChallenge, codeChallengeMethod] = await (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey);
729
+ [codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod();
729
730
  }
730
731
  res = await (0, fetch_1._request)(this.fetch, 'POST', `${this.url}/signup`, {
731
732
  headers: this.headers,
732
- redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
733
+ redirectTo: this._maybeAppendFlowIdToRedirect(options === null || options === void 0 ? void 0 : options.emailRedirectTo, flowId),
733
734
  body: {
734
735
  email,
735
736
  password,
@@ -760,7 +761,7 @@ class GoTrueClient {
760
761
  }
761
762
  const { data, error } = res;
762
763
  if (error || !data) {
763
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
764
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
764
765
  return this._returnResult({ data: { user: null, session: null }, error: error });
765
766
  }
766
767
  const session = data.session;
@@ -772,7 +773,7 @@ class GoTrueClient {
772
773
  return this._returnResult({ data: { user, session }, error: null });
773
774
  }
774
775
  catch (error) {
775
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
776
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
776
777
  if ((0, errors_1.isAuthError)(error)) {
777
778
  return this._returnResult({ data: { user: null, session: null }, error });
778
779
  }
@@ -987,7 +988,8 @@ class GoTrueClient {
987
988
  * {
988
989
  * data: {
989
990
  * provider: 'github',
990
- * url: <PROVIDER_URL_TO_REDIRECT_TO>
991
+ * url: <PROVIDER_URL_TO_REDIRECT_TO>,
992
+ * flowId: <PKCE_FLOW_ID_OR_NULL>
991
993
  * },
992
994
  * error: null
993
995
  * }
@@ -1060,12 +1062,29 @@ class GoTrueClient {
1060
1062
  *
1061
1063
  * @remarks
1062
1064
  * - Used when `flowType` is set to `pkce` in client options.
1065
+ * - When several PKCE flows are in flight at once, pass `options.flowId` so
1066
+ * the code is exchanged with the verifier created by that specific flow.
1067
+ * The flow id is returned by `signInWithOAuth`, and with
1068
+ * `experimental.appendPkceFlowIdToRedirects` enabled it also arrives on
1069
+ * your callback URL as the reserved `sb_flow_id` query parameter (read
1070
+ * automatically in a browser).
1071
+ * - When a flow id is present but its stored verifier is gone (evicted,
1072
+ * already used, or from another device), the call fails with a verifier
1073
+ * missing error instead of trying another flow's verifier — a mismatched
1074
+ * verifier would consume the single-use code. Without any flow id the
1075
+ * most recently stored verifier is used, as before.
1063
1076
  *
1064
1077
  * @example Exchange Auth Code
1065
1078
  * ```js
1066
1079
  * supabase.auth.exchangeCodeForSession('34e770dd-9ff9-416c-87fa-43b31d7ef225')
1067
1080
  * ```
1068
1081
  *
1082
+ * @example Exchange Auth Code for a specific flow (e.g. in a server-side callback handler)
1083
+ * ```js
1084
+ * const flowId = requestUrl.searchParams.get('sb_flow_id')
1085
+ * supabase.auth.exchangeCodeForSession(code, flowId ? { flowId } : undefined)
1086
+ * ```
1087
+ *
1069
1088
  * @exampleResponse Exchange Auth Code
1070
1089
  * ```json
1071
1090
  * {
@@ -1223,15 +1242,15 @@ class GoTrueClient {
1223
1242
  * }
1224
1243
  * ```
1225
1244
  */
1226
- async exchangeCodeForSession(authCode) {
1245
+ async exchangeCodeForSession(authCode, options) {
1227
1246
  await this.initializePromise;
1228
1247
  if (this.lock != null) {
1229
1248
  // TODO(v3): remove legacy lock path
1230
1249
  return this._acquireLock(this.lockAcquireTimeout, async () => {
1231
- return this._exchangeCodeForSession(authCode);
1250
+ return this._exchangeCodeForSession(authCode, options);
1232
1251
  });
1233
1252
  }
1234
- return this._exchangeCodeForSession(authCode);
1253
+ return this._exchangeCodeForSession(authCode, options);
1235
1254
  }
1236
1255
  /**
1237
1256
  * Signs in a user by verifying a message signed by the user's private key.
@@ -1573,8 +1592,23 @@ class GoTrueClient {
1573
1592
  throw error;
1574
1593
  }
1575
1594
  }
1576
- async _exchangeCodeForSession(authCode) {
1577
- const storageItem = await (0, helpers_1.getItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
1595
+ async _exchangeCodeForSession(authCode, options) {
1596
+ const hasExplicitFlowId = (options === null || options === void 0 ? void 0 : options.flowId) != null;
1597
+ const requestedFlowId = hasExplicitFlowId
1598
+ ? (0, helpers_1.validatePKCEFlowId)(options === null || options === void 0 ? void 0 : options.flowId)
1599
+ : (0, helpers_1.isBrowser)()
1600
+ ? (0, helpers_1.validatePKCEFlowId)((0, helpers_1.parseParametersFromURL)(window.location.href)[constants_1.PKCE_FLOW_ID_PARAM])
1601
+ : null;
1602
+ if (hasExplicitFlowId && !requestedFlowId) {
1603
+ this._debug('#_exchangeCodeForSession()', 'provided flowId is not a valid flow id', options === null || options === void 0 ? void 0 : options.flowId);
1604
+ }
1605
+ // With a flow id (explicit or from the callback URL) the lookup is
1606
+ // slot-only and a miss fails fast — see retrievePKCEVerifier. An invalid
1607
+ // explicit flow id also fails fast rather than borrowing another flow's
1608
+ // verifier.
1609
+ const { verifier: storageItem, flowId } = hasExplicitFlowId && !requestedFlowId
1610
+ ? { verifier: null, flowId: null }
1611
+ : await (0, helpers_1.retrievePKCEVerifier)(this.storage, this.storageKey, requestedFlowId);
1578
1612
  const [codeVerifier, redirectType] = (storageItem !== null && storageItem !== void 0 ? storageItem : '').split('/');
1579
1613
  try {
1580
1614
  if (!codeVerifier && this.flowType === 'pkce') {
@@ -1588,7 +1622,7 @@ class GoTrueClient {
1588
1622
  },
1589
1623
  xform: fetch_1._sessionResponse,
1590
1624
  });
1591
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
1625
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
1592
1626
  if (error) {
1593
1627
  throw error;
1594
1628
  }
@@ -1606,7 +1640,7 @@ class GoTrueClient {
1606
1640
  return this._returnResult({ data: Object.assign(Object.assign({}, data), { redirectType: redirectType !== null && redirectType !== void 0 ? redirectType : null }), error });
1607
1641
  }
1608
1642
  catch (error) {
1609
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
1643
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
1610
1644
  if ((0, errors_1.isAuthError)(error)) {
1611
1645
  return this._returnResult({
1612
1646
  data: { user: null, session: null, redirectType: null },
@@ -1805,6 +1839,7 @@ class GoTrueClient {
1805
1839
  */
1806
1840
  async signInWithOtp(credentials) {
1807
1841
  var _a, _b, _c, _d, _f;
1842
+ let flowId = null;
1808
1843
  try {
1809
1844
  if ('email' in credentials) {
1810
1845
  const { email, options } = credentials;
@@ -1812,7 +1847,7 @@ class GoTrueClient {
1812
1847
  let codeChallengeMethod = null;
1813
1848
  if (this.flowType === 'pkce') {
1814
1849
  ;
1815
- [codeChallenge, codeChallengeMethod] = await (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey);
1850
+ [codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod();
1816
1851
  }
1817
1852
  const { error } = await (0, fetch_1._request)(this.fetch, 'POST', `${this.url}/otp`, {
1818
1853
  headers: this.headers,
@@ -1824,7 +1859,7 @@ class GoTrueClient {
1824
1859
  code_challenge: codeChallenge,
1825
1860
  code_challenge_method: codeChallengeMethod,
1826
1861
  },
1827
- redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
1862
+ redirectTo: this._maybeAppendFlowIdToRedirect(options === null || options === void 0 ? void 0 : options.emailRedirectTo, flowId),
1828
1863
  });
1829
1864
  return this._returnResult({ data: { user: null, session: null }, error });
1830
1865
  }
@@ -1848,7 +1883,7 @@ class GoTrueClient {
1848
1883
  throw new errors_1.AuthInvalidCredentialsError('You must provide either an email or phone number.');
1849
1884
  }
1850
1885
  catch (error) {
1851
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
1886
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
1852
1887
  if ((0, errors_1.isAuthError)(error)) {
1853
1888
  return this._returnResult({ data: { user: null, session: null }, error });
1854
1889
  }
@@ -2083,29 +2118,30 @@ class GoTrueClient {
2083
2118
  * ```
2084
2119
  */
2085
2120
  async signInWithSSO(params) {
2086
- var _a, _b, _c, _d, _f;
2121
+ var _a, _b, _c, _d;
2122
+ let flowId = null;
2087
2123
  try {
2088
2124
  let codeChallenge = null;
2089
2125
  let codeChallengeMethod = null;
2090
2126
  if (this.flowType === 'pkce') {
2091
2127
  ;
2092
- [codeChallenge, codeChallengeMethod] = await (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey);
2128
+ [codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod();
2093
2129
  }
2094
2130
  const result = await (0, fetch_1._request)(this.fetch, 'POST', `${this.url}/sso`, {
2095
- body: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, ('providerId' in params ? { provider_id: params.providerId } : null)), ('domain' in params ? { domain: params.domain } : null)), { redirect_to: (_b = (_a = params.options) === null || _a === void 0 ? void 0 : _a.redirectTo) !== null && _b !== void 0 ? _b : undefined }), (((_c = params === null || params === void 0 ? void 0 : params.options) === null || _c === void 0 ? void 0 : _c.captchaToken)
2131
+ body: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, ('providerId' in params ? { provider_id: params.providerId } : null)), ('domain' in params ? { domain: params.domain } : null)), { redirect_to: this._maybeAppendFlowIdToRedirect((_a = params.options) === null || _a === void 0 ? void 0 : _a.redirectTo, flowId) }), (((_b = params === null || params === void 0 ? void 0 : params.options) === null || _b === void 0 ? void 0 : _b.captchaToken)
2096
2132
  ? { gotrue_meta_security: { captcha_token: params.options.captchaToken } }
2097
2133
  : null)), { skip_http_redirect: true, code_challenge: codeChallenge, code_challenge_method: codeChallengeMethod }),
2098
2134
  headers: this.headers,
2099
2135
  xform: fetch_1._ssoResponse,
2100
2136
  });
2101
2137
  // Automatically redirect in browser unless skipBrowserRedirect is true
2102
- if (((_d = result.data) === null || _d === void 0 ? void 0 : _d.url) && (0, helpers_1.isBrowser)() && !((_f = params.options) === null || _f === void 0 ? void 0 : _f.skipBrowserRedirect)) {
2138
+ if (((_c = result.data) === null || _c === void 0 ? void 0 : _c.url) && (0, helpers_1.isBrowser)() && !((_d = params.options) === null || _d === void 0 ? void 0 : _d.skipBrowserRedirect)) {
2103
2139
  window.location.assign(result.data.url);
2104
2140
  }
2105
2141
  return this._returnResult(result);
2106
2142
  }
2107
2143
  catch (error) {
2108
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
2144
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
2109
2145
  if ((0, errors_1.isAuthError)(error)) {
2110
2146
  return this._returnResult({ data: null, error });
2111
2147
  }
@@ -2225,6 +2261,7 @@ class GoTrueClient {
2225
2261
  * ```
2226
2262
  */
2227
2263
  async resend(credentials) {
2264
+ let flowId = null;
2228
2265
  try {
2229
2266
  const endpoint = `${this.url}/resend`;
2230
2267
  if ('email' in credentials) {
@@ -2233,7 +2270,7 @@ class GoTrueClient {
2233
2270
  let codeChallengeMethod = null;
2234
2271
  if (this.flowType === 'pkce') {
2235
2272
  ;
2236
- [codeChallenge, codeChallengeMethod] = await (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey);
2273
+ [codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod();
2237
2274
  }
2238
2275
  const { error } = await (0, fetch_1._request)(this.fetch, 'POST', endpoint, {
2239
2276
  headers: this.headers,
@@ -2244,10 +2281,10 @@ class GoTrueClient {
2244
2281
  code_challenge: codeChallenge,
2245
2282
  code_challenge_method: codeChallengeMethod,
2246
2283
  },
2247
- redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
2284
+ redirectTo: this._maybeAppendFlowIdToRedirect(options === null || options === void 0 ? void 0 : options.emailRedirectTo, flowId),
2248
2285
  });
2249
2286
  if (error) {
2250
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
2287
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
2251
2288
  }
2252
2289
  return this._returnResult({ data: { user: null, session: null }, error });
2253
2290
  }
@@ -2269,7 +2306,7 @@ class GoTrueClient {
2269
2306
  throw new errors_1.AuthInvalidCredentialsError('You must provide either an email or phone number and a type');
2270
2307
  }
2271
2308
  catch (error) {
2272
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
2309
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
2273
2310
  if ((0, errors_1.isAuthError)(error)) {
2274
2311
  return this._returnResult({ data: { user: null, session: null }, error });
2275
2312
  }
@@ -2671,7 +2708,6 @@ class GoTrueClient {
2671
2708
  // JWT contains a `session_id` which does not correspond to an active
2672
2709
  // session in the database, indicating the user is signed out.
2673
2710
  await this._removeSession();
2674
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
2675
2711
  }
2676
2712
  return this._returnResult({ data: { user: null }, error });
2677
2713
  }
@@ -2803,6 +2839,7 @@ class GoTrueClient {
2803
2839
  return await this._updateUser(attributes, options);
2804
2840
  }
2805
2841
  async _updateUser(attributes, options = {}) {
2842
+ let flowId = null;
2806
2843
  try {
2807
2844
  return await this._useSession(async (result) => {
2808
2845
  const { data: sessionData, error: sessionError } = result;
@@ -2817,11 +2854,11 @@ class GoTrueClient {
2817
2854
  let codeChallengeMethod = null;
2818
2855
  if (this.flowType === 'pkce' && attributes.email != null) {
2819
2856
  ;
2820
- [codeChallenge, codeChallengeMethod] = await (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey);
2857
+ [codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod();
2821
2858
  }
2822
2859
  const { data, error: userError } = await (0, fetch_1._request)(this.fetch, 'PUT', `${this.url}/user`, {
2823
2860
  headers: this.headers,
2824
- redirectTo: options === null || options === void 0 ? void 0 : options.emailRedirectTo,
2861
+ redirectTo: this._maybeAppendFlowIdToRedirect(options === null || options === void 0 ? void 0 : options.emailRedirectTo, flowId),
2825
2862
  body: Object.assign(Object.assign({}, attributes), { code_challenge: codeChallenge, code_challenge_method: codeChallengeMethod }),
2826
2863
  jwt: session.access_token,
2827
2864
  xform: fetch_1._userResponse,
@@ -2836,7 +2873,7 @@ class GoTrueClient {
2836
2873
  });
2837
2874
  }
2838
2875
  catch (error) {
2839
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
2876
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
2840
2877
  if ((0, errors_1.isAuthError)(error)) {
2841
2878
  return this._returnResult({ data: { user: null }, error });
2842
2879
  }
@@ -3229,11 +3266,14 @@ class GoTrueClient {
3229
3266
  this._debug('#_initialize()', 'begin', 'is PKCE flow', true);
3230
3267
  if (!params.code)
3231
3268
  throw new errors_1.AuthPKCEGrantCodeExchangeError('No code detected.');
3232
- const { data, error } = await this._exchangeCodeForSession(params.code);
3269
+ const { data, error } = await this._exchangeCodeForSession(params.code, {
3270
+ flowId: params[constants_1.PKCE_FLOW_ID_PARAM],
3271
+ });
3233
3272
  if (error)
3234
3273
  throw error;
3235
3274
  const url = new URL(window.location.href);
3236
3275
  url.searchParams.delete('code');
3276
+ url.searchParams.delete(constants_1.PKCE_FLOW_ID_PARAM);
3237
3277
  window.history.replaceState(window.history.state, '', url.toString());
3238
3278
  return {
3239
3279
  data: { session: data.session, redirectType: (_a = data.redirectType) !== null && _a !== void 0 ? _a : null },
@@ -3303,8 +3343,16 @@ class GoTrueClient {
3303
3343
  * Checks if the current URL and backing storage contain parameters given by a PKCE flow
3304
3344
  */
3305
3345
  async _isPKCECallback(params) {
3346
+ if (!params.code) {
3347
+ return false;
3348
+ }
3349
+ const flowId = (0, helpers_1.validatePKCEFlowId)(params[constants_1.PKCE_FLOW_ID_PARAM]);
3350
+ if (flowId &&
3351
+ (await (0, helpers_1.getItemAsync)(this.storage, (0, helpers_1.pkceVerifierSlotKey)(this.storageKey, flowId)))) {
3352
+ return true;
3353
+ }
3306
3354
  const currentStorageContent = await (0, helpers_1.getItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
3307
- return !!(params.code && currentStorageContent);
3355
+ return !!currentStorageContent;
3308
3356
  }
3309
3357
  /**
3310
3358
  * Inside a browser context, `signOut()` will remove the logged in user from the browser session and log them out - removing all items from localstorage and then trigger a `"SIGNED_OUT"` event.
@@ -3362,7 +3410,6 @@ class GoTrueClient {
3362
3410
  var _a;
3363
3411
  const removeCurrentSession = async () => {
3364
3412
  await this._removeSession();
3365
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
3366
3413
  };
3367
3414
  const { data, error: sessionError } = result;
3368
3415
  if (sessionError && !(0, errors_1.isAuthSessionMissingError)(sessionError)) {
@@ -3606,10 +3653,16 @@ class GoTrueClient {
3606
3653
  catch (err) {
3607
3654
  await ((_b = this.stateChangeEmitters.get(id)) === null || _b === void 0 ? void 0 : _b.callback('INITIAL_SESSION', null));
3608
3655
  this._debug('INITIAL_SESSION', 'callback id', id, 'error', err);
3609
- if ((0, errors_1.isAuthSessionMissingError)(err) || (0, errors_1.isAuthRetryableFetchError)(err)) {
3610
- // A missing session or a transient/aborted network failure (e.g. a
3611
- // superseded page navigation cancelling the in-flight request) is
3612
- // not an application error — warn rather than surface it raw.
3656
+ if ((0, errors_1.isAuthSessionMissingError)(err) ||
3657
+ (0, errors_1.isAuthRetryableFetchError)(err) ||
3658
+ ((0, errors_1.isAuthApiError)(err) &&
3659
+ (err.code === 'refresh_token_not_found' ||
3660
+ err.code === 'refresh_token_already_used' ||
3661
+ err.code === 'session_expired'))) {
3662
+ // A missing session, a transient/aborted network failure (e.g. a
3663
+ // superseded page navigation cancelling the in-flight request), or
3664
+ // a dead refresh token (e.g. stale SSR cookies) is not an
3665
+ // application error — warn rather than surface it raw.
3613
3666
  console.warn(err);
3614
3667
  }
3615
3668
  else {
@@ -3688,9 +3741,10 @@ class GoTrueClient {
3688
3741
  async resetPasswordForEmail(email, options = {}) {
3689
3742
  let codeChallenge = null;
3690
3743
  let codeChallengeMethod = null;
3744
+ let flowId = null;
3691
3745
  if (this.flowType === 'pkce') {
3692
3746
  ;
3693
- [codeChallenge, codeChallengeMethod] = await (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey, true // isPasswordRecovery
3747
+ [codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod(true // isPasswordRecovery
3694
3748
  );
3695
3749
  }
3696
3750
  try {
@@ -3702,11 +3756,11 @@ class GoTrueClient {
3702
3756
  gotrue_meta_security: { captcha_token: options.captchaToken },
3703
3757
  },
3704
3758
  headers: this.headers,
3705
- redirectTo: options.redirectTo,
3759
+ redirectTo: this._maybeAppendFlowIdToRedirect(options.redirectTo, flowId),
3706
3760
  });
3707
3761
  }
3708
3762
  catch (error) {
3709
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
3763
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, flowId);
3710
3764
  if ((0, errors_1.isAuthError)(error)) {
3711
3765
  return this._returnResult({ data: null, error });
3712
3766
  }
@@ -3789,7 +3843,8 @@ class GoTrueClient {
3789
3843
  * {
3790
3844
  * data: {
3791
3845
  * provider: 'github',
3792
- * url: <PROVIDER_URL_TO_REDIRECT_TO>
3846
+ * url: <PROVIDER_URL_TO_REDIRECT_TO>,
3847
+ * flowId: <PKCE_FLOW_ID_OR_NULL>
3793
3848
  * },
3794
3849
  * error: null
3795
3850
  * }
@@ -3803,18 +3858,20 @@ class GoTrueClient {
3803
3858
  }
3804
3859
  async linkIdentityOAuth(credentials) {
3805
3860
  var _a;
3861
+ let flowId = null;
3806
3862
  try {
3807
3863
  const { data, error } = await this._useSession(async (result) => {
3808
3864
  var _a, _b, _c, _d, _f;
3809
3865
  const { data, error } = result;
3810
3866
  if (error)
3811
3867
  throw error;
3812
- const url = await this._getUrlForProvider(`${this.url}/user/identities/authorize`, credentials.provider, {
3868
+ const { url, flowId: urlFlowId } = await this._getUrlForProvider(`${this.url}/user/identities/authorize`, credentials.provider, {
3813
3869
  redirectTo: (_a = credentials.options) === null || _a === void 0 ? void 0 : _a.redirectTo,
3814
3870
  scopes: (_b = credentials.options) === null || _b === void 0 ? void 0 : _b.scopes,
3815
3871
  queryParams: (_c = credentials.options) === null || _c === void 0 ? void 0 : _c.queryParams,
3816
3872
  skipBrowserRedirect: true,
3817
3873
  });
3874
+ flowId = urlFlowId;
3818
3875
  return await (0, fetch_1._request)(this.fetch, 'GET', url, {
3819
3876
  headers: this.headers,
3820
3877
  jwt: (_f = (_d = data.session) === null || _d === void 0 ? void 0 : _d.access_token) !== null && _f !== void 0 ? _f : undefined,
@@ -3826,13 +3883,16 @@ class GoTrueClient {
3826
3883
  window.location.assign(data === null || data === void 0 ? void 0 : data.url);
3827
3884
  }
3828
3885
  return this._returnResult({
3829
- data: { provider: credentials.provider, url: data === null || data === void 0 ? void 0 : data.url },
3886
+ data: { provider: credentials.provider, url: data === null || data === void 0 ? void 0 : data.url, flowId },
3830
3887
  error: null,
3831
3888
  });
3832
3889
  }
3833
3890
  catch (error) {
3834
3891
  if ((0, errors_1.isAuthError)(error)) {
3835
- return this._returnResult({ data: { provider: credentials.provider, url: null }, error });
3892
+ return this._returnResult({
3893
+ data: { provider: credentials.provider, url: null, flowId },
3894
+ error,
3895
+ });
3836
3896
  }
3837
3897
  throw error;
3838
3898
  }
@@ -3875,7 +3935,7 @@ class GoTrueClient {
3875
3935
  return this._returnResult({ data, error });
3876
3936
  }
3877
3937
  catch (error) {
3878
- await (0, helpers_1.removeItemAsync)(this.storage, `${this.storageKey}-code-verifier`);
3938
+ await (0, helpers_1.removePKCEVerifier)(this.storage, this.storageKey, null);
3879
3939
  if ((0, errors_1.isAuthError)(error)) {
3880
3940
  return this._returnResult({ data: { user: null, session: null }, error });
3881
3941
  }
@@ -3980,7 +4040,7 @@ class GoTrueClient {
3980
4040
  return isValidSession;
3981
4041
  }
3982
4042
  async _handleProviderSignIn(provider, options) {
3983
- const url = await this._getUrlForProvider(`${this.url}/authorize`, provider, {
4043
+ const { url, flowId } = await this._getUrlForProvider(`${this.url}/authorize`, provider, {
3984
4044
  redirectTo: options.redirectTo,
3985
4045
  scopes: options.scopes,
3986
4046
  queryParams: options.queryParams,
@@ -3990,7 +4050,7 @@ class GoTrueClient {
3990
4050
  if ((0, helpers_1.isBrowser)() && !options.skipBrowserRedirect) {
3991
4051
  window.location.assign(url);
3992
4052
  }
3993
- return { data: { provider, url }, error: null };
4053
+ return { data: { provider, url, flowId }, error: null };
3994
4054
  }
3995
4055
  /**
3996
4056
  * Recovers the session from LocalStorage and refreshes the token
@@ -4338,7 +4398,7 @@ class GoTrueClient {
4338
4398
  this.lastRefreshFailure = null;
4339
4399
  this.suppressGetSessionWarning = false;
4340
4400
  await (0, helpers_1.removeItemAsync)(this.storage, this.storageKey);
4341
- await (0, helpers_1.removeItemAsync)(this.storage, this.storageKey + '-code-verifier');
4401
+ await (0, helpers_1.removeAllPKCEVerifiers)(this.storage, this.storageKey);
4342
4402
  await (0, helpers_1.removeItemAsync)(this.storage, this.storageKey + '-user');
4343
4403
  if (this.userStorage) {
4344
4404
  await (0, helpers_1.removeItemAsync)(this.userStorage, this.storageKey + '-user');
@@ -4704,15 +4764,23 @@ class GoTrueClient {
4704
4764
  * @param options.queryParams An object of key-value pairs containing query parameters granted to the OAuth application.
4705
4765
  */
4706
4766
  async _getUrlForProvider(url, provider, options) {
4767
+ let redirectTo = options === null || options === void 0 ? void 0 : options.redirectTo;
4768
+ let codeChallenge = null;
4769
+ let codeChallengeMethod = null;
4770
+ let flowId = null;
4771
+ if (this.flowType === 'pkce') {
4772
+ ;
4773
+ [codeChallenge, codeChallengeMethod, flowId] = await this._getCodeChallengeAndMethod();
4774
+ redirectTo = this._maybeAppendFlowIdToRedirect(redirectTo, flowId);
4775
+ }
4707
4776
  const urlParams = [`provider=${encodeURIComponent(provider)}`];
4708
- if (options === null || options === void 0 ? void 0 : options.redirectTo) {
4709
- urlParams.push(`redirect_to=${encodeURIComponent(options.redirectTo)}`);
4777
+ if (redirectTo) {
4778
+ urlParams.push(`redirect_to=${encodeURIComponent(redirectTo)}`);
4710
4779
  }
4711
4780
  if (options === null || options === void 0 ? void 0 : options.scopes) {
4712
4781
  urlParams.push(`scopes=${encodeURIComponent(options.scopes)}`);
4713
4782
  }
4714
- if (this.flowType === 'pkce') {
4715
- const [codeChallenge, codeChallengeMethod] = await (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey);
4783
+ if (codeChallenge != null && codeChallengeMethod != null) {
4716
4784
  const flowParams = new URLSearchParams({
4717
4785
  code_challenge: `${encodeURIComponent(codeChallenge)}`,
4718
4786
  code_challenge_method: `${encodeURIComponent(codeChallengeMethod)}`,
@@ -4726,7 +4794,27 @@ class GoTrueClient {
4726
4794
  if (options === null || options === void 0 ? void 0 : options.skipBrowserRedirect) {
4727
4795
  urlParams.push(`skip_http_redirect=${options.skipBrowserRedirect}`);
4728
4796
  }
4729
- return `${url}?${urlParams.join('&')}`;
4797
+ return { url: `${url}?${urlParams.join('&')}`, flowId };
4798
+ }
4799
+ /**
4800
+ * Appends the reserved flow id parameter to a redirect URL so the callback
4801
+ * can be matched to the verifier stored for its flow. Opt-in via
4802
+ * `experimental.appendPkceFlowIdToRedirects`: redirect URLs are validated
4803
+ * against the project's allow list including the query string, so an extra
4804
+ * parameter can stop exact (non-wildcard) entries from matching.
4805
+ */
4806
+ _maybeAppendFlowIdToRedirect(redirectTo, flowId) {
4807
+ if (!redirectTo || !flowId || !this.experimental.appendPkceFlowIdToRedirects) {
4808
+ return redirectTo !== null && redirectTo !== void 0 ? redirectTo : undefined;
4809
+ }
4810
+ return (0, helpers_1.appendFlowIdToRedirectTo)(redirectTo, flowId);
4811
+ }
4812
+ /**
4813
+ * Generates and stores a PKCE challenge/verifier pair for a new flow,
4814
+ * logging any pending verifier the bounded slot ring evicts.
4815
+ */
4816
+ async _getCodeChallengeAndMethod(isPasswordRecovery = false) {
4817
+ return (0, helpers_1.getCodeChallengeAndMethod)(this.storage, this.storageKey, isPasswordRecovery, (evictedFlowId) => this._debug('#_getCodeChallengeAndMethod()', 'evicted oldest pending PKCE verifier slot', evictedFlowId));
4730
4818
  }
4731
4819
  async _unenroll(params) {
4732
4820
  try {