@supabase/gotrue-js 2.45.0 → 2.46.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.
@@ -18,7 +18,6 @@ import {
18
18
  decodeJWTPayload,
19
19
  Deferred,
20
20
  getItemAsync,
21
- getParameterByName,
22
21
  isBrowser,
23
22
  removeItemAsync,
24
23
  resolveFetch,
@@ -32,6 +31,7 @@ import {
32
31
  stackGuard,
33
32
  isInStackGuard,
34
33
  stackGuardsSupported,
34
+ parseParametersFromURL,
35
35
  } from './lib/helpers'
36
36
  import localStorageAdapter from './lib/local-storage'
37
37
  import { polyfillGlobalThis } from './lib/polyfills'
@@ -272,7 +272,7 @@ export default class GoTrueClient {
272
272
  this._debug('#_initialize()', 'begin', 'is PKCE flow', isPKCEFlow)
273
273
 
274
274
  if (isPKCEFlow || (this.detectSessionInUrl && this._isImplicitGrantFlow())) {
275
- const { data, error } = await this._getSessionFromUrl(isPKCEFlow)
275
+ const { data, error } = await this._getSessionFromURL(isPKCEFlow)
276
276
  if (error) {
277
277
  this._debug('#_initialize()', 'error detecting session from URL', error)
278
278
 
@@ -1176,7 +1176,7 @@ export default class GoTrueClient {
1176
1176
  /**
1177
1177
  * Gets the session data from a URL string
1178
1178
  */
1179
- private async _getSessionFromUrl(isPKCEFlow: boolean): Promise<
1179
+ private async _getSessionFromURL(isPKCEFlow: boolean): Promise<
1180
1180
  | {
1181
1181
  data: { session: Session; redirectType: string | null }
1182
1182
  error: null
@@ -1190,62 +1190,68 @@ export default class GoTrueClient {
1190
1190
  } else if (this.flowType == 'pkce' && !isPKCEFlow) {
1191
1191
  throw new AuthPKCEGrantCodeExchangeError('Not a valid PKCE flow url.')
1192
1192
  }
1193
+
1194
+ const params = parseParametersFromURL(window.location.href)
1195
+
1193
1196
  if (isPKCEFlow) {
1194
- const authCode = getParameterByName('code')
1195
- if (!authCode) throw new AuthPKCEGrantCodeExchangeError('No code detected.')
1196
- const { data, error } = await this.exchangeCodeForSession(authCode)
1197
+ if (!params.code) throw new AuthPKCEGrantCodeExchangeError('No code detected.')
1198
+ const { data, error } = await this.exchangeCodeForSession(params.code)
1197
1199
  if (error) throw error
1198
- if (!data.session) throw new AuthPKCEGrantCodeExchangeError('No session detected.')
1199
- let url = new URL(window.location.href)
1200
+
1201
+ const url = new URL(window.location.href)
1200
1202
  url.searchParams.delete('code')
1203
+
1201
1204
  window.history.replaceState(window.history.state, '', url.toString())
1205
+
1202
1206
  return { data: { session: data.session, redirectType: null }, error: null }
1203
1207
  }
1204
1208
 
1205
- const error_description = getParameterByName('error_description')
1206
- if (error_description) {
1207
- const error_code = getParameterByName('error_code')
1208
- if (!error_code) throw new AuthImplicitGrantRedirectError('No error_code detected.')
1209
- const error = getParameterByName('error')
1210
- if (!error) throw new AuthImplicitGrantRedirectError('No error detected.')
1211
-
1212
- throw new AuthImplicitGrantRedirectError(error_description, { error, code: error_code })
1209
+ if (params.error || params.error_description || params.error_code) {
1210
+ throw new AuthImplicitGrantRedirectError(
1211
+ params.error_description || 'Error in URL with unspecified error_description',
1212
+ {
1213
+ error: params.error || 'unspecified_error',
1214
+ code: params.error_code || 'unspecified_code',
1215
+ }
1216
+ )
1213
1217
  }
1214
1218
 
1215
- const provider_token = getParameterByName('provider_token')
1216
- const provider_refresh_token = getParameterByName('provider_refresh_token')
1217
- const access_token = getParameterByName('access_token')
1218
- if (!access_token) throw new AuthImplicitGrantRedirectError('No access_token detected.')
1219
- const expires_in = getParameterByName('expires_in')
1220
- if (!expires_in) throw new AuthImplicitGrantRedirectError('No expires_in detected.')
1221
- const refresh_token = getParameterByName('refresh_token')
1222
- if (!refresh_token) throw new AuthImplicitGrantRedirectError('No refresh_token detected.')
1223
- const token_type = getParameterByName('token_type')
1224
- if (!token_type) throw new AuthImplicitGrantRedirectError('No token_type detected.')
1219
+ const {
1220
+ provider_token,
1221
+ provider_refresh_token,
1222
+ access_token,
1223
+ refresh_token,
1224
+ expires_in,
1225
+ token_type,
1226
+ } = params
1227
+
1228
+ if (!access_token || !expires_in || !refresh_token || !token_type) {
1229
+ throw new AuthImplicitGrantRedirectError('No session defined in URL')
1230
+ }
1225
1231
 
1226
1232
  const timeNow = Math.round(Date.now() / 1000)
1227
- const expires_at = timeNow + parseInt(expires_in)
1233
+ const expiresIn = parseInt(expires_in)
1234
+ const expires_at = timeNow + expiresIn
1228
1235
 
1229
1236
  const { data, error } = await this.getUser(access_token)
1230
1237
  if (error) throw error
1231
- const user: User = data.user
1238
+
1232
1239
  const session: Session = {
1233
1240
  provider_token,
1234
1241
  provider_refresh_token,
1235
1242
  access_token,
1236
- expires_in: parseInt(expires_in),
1243
+ expires_in: expiresIn,
1237
1244
  expires_at,
1238
1245
  refresh_token,
1239
1246
  token_type,
1240
- user,
1247
+ user: data.user!!,
1241
1248
  }
1242
- const redirectType = getParameterByName('type')
1243
1249
 
1244
1250
  // Remove tokens from URL
1245
1251
  window.location.hash = ''
1246
- this._debug('#_getSessionFromUrl()', 'clearing window.location.hash')
1252
+ this._debug('#_getSessionFromURL()', 'clearing window.location.hash')
1247
1253
 
1248
- return { data: { session, redirectType }, error: null }
1254
+ return { data: { session, redirectType: params.type }, error: null }
1249
1255
  } catch (error) {
1250
1256
  if (isAuthError(error)) {
1251
1257
  return { data: { session: null, redirectType: null }, error }
@@ -1259,21 +1265,22 @@ export default class GoTrueClient {
1259
1265
  * Checks if the current URL contains parameters given by an implicit oauth grant flow (https://www.rfc-editor.org/rfc/rfc6749.html#section-4.2)
1260
1266
  */
1261
1267
  private _isImplicitGrantFlow(): boolean {
1262
- return (
1263
- isBrowser() &&
1264
- (Boolean(getParameterByName('access_token')) ||
1265
- Boolean(getParameterByName('error_description')))
1266
- )
1268
+ const params = parseParametersFromURL(window.location.href)
1269
+
1270
+ return !!(isBrowser() && (params.access_token || params.error_description))
1267
1271
  }
1268
1272
  /**
1269
1273
  * Checks if the current URL and backing storage contain parameters given by a PKCE flow
1270
1274
  */
1271
1275
  private async _isPKCEFlow(): Promise<boolean> {
1276
+ const params = parseParametersFromURL(window.location.href)
1277
+
1272
1278
  const currentStorageContent = await getItemAsync(
1273
1279
  this.storage,
1274
1280
  `${this.storageKey}-code-verifier`
1275
1281
  )
1276
- return Boolean(getParameterByName('code')) && Boolean(currentStorageContent)
1282
+
1283
+ return !!(params.code && currentStorageContent)
1277
1284
  }
1278
1285
 
1279
1286
  /**
@@ -59,15 +59,31 @@ export const supportsLocalStorage = () => {
59
59
  return localStorageWriteTests.writable
60
60
  }
61
61
 
62
- export function getParameterByName(name: string, url?: string) {
63
- if (!url) url = window?.location?.href || ''
64
- // eslint-disable-next-line no-useless-escape
65
- name = name.replace(/[\[\]]/g, '\\$&')
66
- const regex = new RegExp('[?&#]' + name + '(=([^&#]*)|&|#|$)'),
67
- results = regex.exec(url)
68
- if (!results) return null
69
- if (!results[2]) return ''
70
- return decodeURIComponent(results[2].replace(/\+/g, ' '))
62
+ /**
63
+ * Extracts parameters encoded in the URL both in the query and fragment.
64
+ */
65
+ export function parseParametersFromURL(href: string) {
66
+ const result: { [parameter: string]: string } = {}
67
+
68
+ const url = new URL(href)
69
+
70
+ if (url.hash && url.hash[0] === '#') {
71
+ try {
72
+ const hashSearchParams = new URLSearchParams(url.hash.substring(1))
73
+ hashSearchParams.forEach((value, key) => {
74
+ result[key] = value
75
+ })
76
+ } catch (e: any) {
77
+ // hash is not a query string
78
+ }
79
+ }
80
+
81
+ // search parameters take precedence over hash parameters
82
+ url.searchParams.forEach((value, key) => {
83
+ result[key] = value
84
+ })
85
+
86
+ return result
71
87
  }
72
88
 
73
89
  type Fetch = typeof fetch
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '2.45.0'
2
+ export const version = '2.46.0'