@supabase/gotrue-js 2.70.0 → 2.71.0-rc.3

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/dist/main/GoTrueClient.d.ts +4 -0
  2. package/dist/main/GoTrueClient.d.ts.map +1 -1
  3. package/dist/main/GoTrueClient.js +103 -6
  4. package/dist/main/GoTrueClient.js.map +1 -1
  5. package/dist/main/lib/helpers.d.ts +2 -1
  6. package/dist/main/lib/helpers.d.ts.map +1 -1
  7. package/dist/main/lib/helpers.js +30 -1
  8. package/dist/main/lib/helpers.js.map +1 -1
  9. package/dist/main/lib/local-storage.d.ts +0 -4
  10. package/dist/main/lib/local-storage.d.ts.map +1 -1
  11. package/dist/main/lib/local-storage.js +1 -25
  12. package/dist/main/lib/local-storage.js.map +1 -1
  13. package/dist/main/lib/types.d.ts +11 -0
  14. package/dist/main/lib/types.d.ts.map +1 -1
  15. package/dist/main/lib/types.js.map +1 -1
  16. package/dist/main/lib/version.d.ts +1 -1
  17. package/dist/main/lib/version.d.ts.map +1 -1
  18. package/dist/main/lib/version.js +1 -1
  19. package/dist/main/lib/version.js.map +1 -1
  20. package/dist/module/GoTrueClient.d.ts +4 -0
  21. package/dist/module/GoTrueClient.d.ts.map +1 -1
  22. package/dist/module/GoTrueClient.js +105 -8
  23. package/dist/module/GoTrueClient.js.map +1 -1
  24. package/dist/module/lib/helpers.d.ts +2 -1
  25. package/dist/module/lib/helpers.d.ts.map +1 -1
  26. package/dist/module/lib/helpers.js +28 -0
  27. package/dist/module/lib/helpers.js.map +1 -1
  28. package/dist/module/lib/local-storage.d.ts +0 -4
  29. package/dist/module/lib/local-storage.d.ts.map +1 -1
  30. package/dist/module/lib/local-storage.js +0 -24
  31. package/dist/module/lib/local-storage.js.map +1 -1
  32. package/dist/module/lib/types.d.ts +11 -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.d.ts.map +1 -1
  37. package/dist/module/lib/version.js +1 -1
  38. package/dist/module/lib/version.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/GoTrueClient.ts +134 -7
  41. package/src/lib/helpers.ts +39 -1
  42. package/src/lib/local-storage.ts +0 -28
  43. package/src/lib/types.ts +12 -0
  44. package/src/lib/version.ts +1 -1
@@ -1,2 +1,2 @@
1
- export declare const version = "2.70.0";
1
+ export declare const version = "2.71.0-rc.3";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/lib/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,WAAW,CAAA"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/lib/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,gBAAgB,CAAA"}
@@ -1,2 +1,2 @@
1
- export const version = '2.70.0';
1
+ export const version = '2.71.0-rc.3';
2
2
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/lib/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAA"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/lib/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supabase/gotrue-js",
3
- "version": "2.70.0",
3
+ "version": "2.71.0-rc.3",
4
4
  "private": false,
5
5
  "description": "Official client library for Supabase Auth",
6
6
  "keywords": [
@@ -41,14 +41,15 @@ import {
41
41
  uuid,
42
42
  retryable,
43
43
  sleep,
44
- supportsLocalStorage,
45
44
  parseParametersFromURL,
46
45
  getCodeChallengeAndMethod,
47
46
  getAlgorithm,
48
47
  validateExp,
49
48
  decodeJWT,
49
+ userNotAvailableProxy,
50
+ supportsLocalStorage,
50
51
  } from './lib/helpers'
51
- import { localStorageAdapter, memoryLocalStorageAdapter } from './lib/local-storage'
52
+ import { memoryLocalStorageAdapter } from './lib/local-storage'
52
53
  import { polyfillGlobalThis } from './lib/polyfills'
53
54
  import { version } from './lib/version'
54
55
  import { LockAcquireTimeoutError, navigatorLock } from './lib/locks'
@@ -114,7 +115,10 @@ import { stringToUint8Array, bytesToBase64URL } from './lib/base64url'
114
115
 
115
116
  polyfillGlobalThis() // Make "globalThis" available
116
117
 
117
- const DEFAULT_OPTIONS: Omit<Required<GoTrueClientOptions>, 'fetch' | 'storage' | 'lock'> = {
118
+ const DEFAULT_OPTIONS: Omit<
119
+ Required<GoTrueClientOptions>,
120
+ 'fetch' | 'storage' | 'userStorage' | 'lock'
121
+ > = {
118
122
  url: GOTRUE_URL,
119
123
  storageKey: STORAGE_KEY,
120
124
  autoRefreshToken: true,
@@ -158,6 +162,10 @@ export default class GoTrueClient {
158
162
  protected autoRefreshToken: boolean
159
163
  protected persistSession: boolean
160
164
  protected storage: SupportedStorage
165
+ /**
166
+ * @experimental
167
+ */
168
+ protected userStorage: SupportedStorage | null = null
161
169
  protected memoryStorage: { [key: string]: string } | null = null
162
170
  protected stateChangeEmitters: Map<string, Subscription> = new Map()
163
171
  protected autoRefreshTicker: ReturnType<typeof setInterval> | null = null
@@ -251,12 +259,16 @@ export default class GoTrueClient {
251
259
  this.storage = settings.storage
252
260
  } else {
253
261
  if (supportsLocalStorage()) {
254
- this.storage = localStorageAdapter
262
+ this.storage = globalThis.localStorage
255
263
  } else {
256
264
  this.memoryStorage = {}
257
265
  this.storage = memoryLocalStorageAdapter(this.memoryStorage)
258
266
  }
259
267
  }
268
+
269
+ if (settings.userStorage) {
270
+ this.userStorage = settings.userStorage
271
+ }
260
272
  } else {
261
273
  this.memoryStorage = {}
262
274
  this.storage = memoryLocalStorageAdapter(this.memoryStorage)
@@ -1347,7 +1359,20 @@ export default class GoTrueClient {
1347
1359
  )
1348
1360
 
1349
1361
  if (!hasExpired) {
1350
- if (this.storage.isServer) {
1362
+ if (this.userStorage) {
1363
+ const maybeUser: { user?: User | null } | null = (await getItemAsync(
1364
+ this.userStorage,
1365
+ this.storageKey + '-user'
1366
+ )) as any
1367
+
1368
+ if (maybeUser?.user) {
1369
+ currentSession.user = maybeUser.user
1370
+ } else {
1371
+ currentSession.user = userNotAvailableProxy()
1372
+ }
1373
+ }
1374
+
1375
+ if (this.storage.isServer && currentSession.user) {
1351
1376
  let suppressWarning = this.suppressGetSessionWarning
1352
1377
  const proxySession: Session = new Proxy(currentSession, {
1353
1378
  get: (target: any, prop: string, receiver: any) => {
@@ -2128,7 +2153,47 @@ export default class GoTrueClient {
2128
2153
  this._debug(debugName, 'begin')
2129
2154
 
2130
2155
  try {
2131
- const currentSession = await getItemAsync(this.storage, this.storageKey)
2156
+ const currentSession: Session = (await getItemAsync(this.storage, this.storageKey)) as any
2157
+
2158
+ if (this.userStorage) {
2159
+ let maybeUser: { user: User | null } | null = (await getItemAsync(
2160
+ this.userStorage,
2161
+ this.storageKey + '-user'
2162
+ )) as any
2163
+
2164
+ if (!this.storage.isServer && Object.is(this.storage, this.userStorage) && !maybeUser) {
2165
+ // storage and userStorage are the same storage medium, for example
2166
+ // window.localStorage if userStorage does not have the user from
2167
+ // storage stored, store it first thereby migrating the user object
2168
+ // from storage -> userStorage
2169
+
2170
+ maybeUser = { user: currentSession.user }
2171
+ await setItemAsync(this.userStorage, this.storageKey + '-user', maybeUser)
2172
+ }
2173
+
2174
+ currentSession.user = maybeUser?.user ?? userNotAvailableProxy()
2175
+ } else if (currentSession && !currentSession.user) {
2176
+ // user storage is not set, let's check if it was previously enabled so
2177
+ // we bring back the storage as it should be
2178
+
2179
+ if (!currentSession.user) {
2180
+ // test if userStorage was previously enabled and the storage medium was the same, to move the user back under the same key
2181
+ const separateUser: { user: User | null } | null = (await getItemAsync(
2182
+ this.storage,
2183
+ this.storageKey + '-user'
2184
+ )) as any
2185
+
2186
+ if (separateUser && separateUser?.user) {
2187
+ currentSession.user = separateUser.user
2188
+
2189
+ await removeItemAsync(this.storage, this.storageKey + '-user')
2190
+ await setItemAsync(this.storage, this.storageKey, currentSession)
2191
+ } else {
2192
+ currentSession.user = userNotAvailableProxy()
2193
+ }
2194
+ }
2195
+ }
2196
+
2132
2197
  this._debug(debugName, 'session from storage', currentSession)
2133
2198
 
2134
2199
  if (!this._isValidSession(currentSession)) {
@@ -2165,6 +2230,29 @@ export default class GoTrueClient {
2165
2230
  }
2166
2231
  }
2167
2232
  }
2233
+ } else if (
2234
+ currentSession.user &&
2235
+ (currentSession.user as any).__isUserNotAvailableProxy === true
2236
+ ) {
2237
+ // If we have a proxy user, try to get the real user data
2238
+ try {
2239
+ const { data, error: userError } = await this._getUser(currentSession.access_token)
2240
+
2241
+ if (!userError && data?.user) {
2242
+ currentSession.user = data.user
2243
+ await this._saveSession(currentSession)
2244
+ await this._notifyAllSubscribers('SIGNED_IN', currentSession)
2245
+ } else {
2246
+ this._debug(debugName, 'could not get user data, skipping SIGNED_IN notification')
2247
+ }
2248
+ } catch (getUserError) {
2249
+ console.error('Error getting user data:', getUserError)
2250
+ this._debug(
2251
+ debugName,
2252
+ 'error getting user data, skipping SIGNED_IN notification',
2253
+ getUserError
2254
+ )
2255
+ }
2168
2256
  } else {
2169
2257
  // no need to persist currentSession again, as we just loaded it from
2170
2258
  // local storage; persisting it again may overwrite a value saved by
@@ -2278,13 +2366,52 @@ export default class GoTrueClient {
2278
2366
  // _saveSession is always called whenever a new session has been acquired
2279
2367
  // so we can safely suppress the warning returned by future getSession calls
2280
2368
  this.suppressGetSessionWarning = true
2281
- await setItemAsync(this.storage, this.storageKey, session)
2369
+
2370
+ // Create a shallow copy to work with, to avoid mutating the original session object if it's used elsewhere
2371
+ const sessionToProcess = { ...session }
2372
+
2373
+ const userIsProxy =
2374
+ sessionToProcess.user && (sessionToProcess.user as any).__isUserNotAvailableProxy === true
2375
+ if (this.userStorage) {
2376
+ if (!userIsProxy && sessionToProcess.user) {
2377
+ // If it's a real user object, save it to userStorage.
2378
+ await setItemAsync(this.userStorage, this.storageKey + '-user', {
2379
+ user: sessionToProcess.user,
2380
+ })
2381
+ } else if (userIsProxy) {
2382
+ // If it's the proxy, it means user was not found in userStorage.
2383
+ // We should ensure no stale user data for this key exists in userStorage if we were to save null,
2384
+ // or simply not save the proxy. For now, we don't save the proxy here.
2385
+ // If there's a need to clear userStorage if user becomes proxy, that logic would go here.
2386
+ }
2387
+
2388
+ // Prepare the main session data for primary storage: remove the user property before cloning
2389
+ // This is important because the original session.user might be the proxy
2390
+ const mainSessionData: Omit<Session, 'user'> & { user?: User } = { ...sessionToProcess }
2391
+ delete mainSessionData.user // Remove user (real or proxy) before cloning for main storage
2392
+
2393
+ const clonedMainSessionData = structuredClone(mainSessionData)
2394
+ await setItemAsync(this.storage, this.storageKey, clonedMainSessionData)
2395
+ } else {
2396
+ // No userStorage is configured.
2397
+ // In this case, session.user should ideally not be a proxy.
2398
+ // If it were, structuredClone would fail. This implies an issue elsewhere if user is a proxy here
2399
+ const clonedSession = structuredClone(sessionToProcess) // sessionToProcess still has its original user property
2400
+ await setItemAsync(this.storage, this.storageKey, clonedSession)
2401
+ }
2282
2402
  }
2283
2403
 
2284
2404
  private async _removeSession() {
2285
2405
  this._debug('#_removeSession()')
2286
2406
 
2287
2407
  await removeItemAsync(this.storage, this.storageKey)
2408
+ await removeItemAsync(this.storage, this.storageKey + '-code-verifier')
2409
+ await removeItemAsync(this.storage, this.storageKey + '-user')
2410
+
2411
+ if (this.userStorage) {
2412
+ await removeItemAsync(this.userStorage, this.storageKey + '-user')
2413
+ }
2414
+
2288
2415
  await this._notifyAllSubscribers('SIGNED_OUT', null)
2289
2416
  }
2290
2417
 
@@ -1,7 +1,7 @@
1
1
  import { API_VERSION_HEADER_NAME, BASE64URL_REGEX } from './constants'
2
2
  import { AuthInvalidJwtError } from './errors'
3
3
  import { base64UrlToUint8Array, stringFromBase64URL } from './base64url'
4
- import { JwtHeader, JwtPayload, SupportedStorage } from './types'
4
+ import { JwtHeader, JwtPayload, SupportedStorage, User } from './types'
5
5
 
6
6
  export function expiresAt(expiresIn: number) {
7
7
  const timeNow = Math.round(Date.now() / 1000)
@@ -365,3 +365,41 @@ export function validateUUID(str: string) {
365
365
  throw new Error('@supabase/auth-js: Expected parameter to be UUID but is not')
366
366
  }
367
367
  }
368
+
369
+ export function userNotAvailableProxy(): User {
370
+ const proxyTarget = {} as User
371
+
372
+ return new Proxy(proxyTarget, {
373
+ get: (target: any, prop: string) => {
374
+ if (prop === '__isUserNotAvailableProxy') {
375
+ return true
376
+ }
377
+ // Preventative check for common problematic symbols during cloning/inspection
378
+ // These symbols might be accessed by structuredClone or other internal mechanisms.
379
+ if (typeof prop === 'symbol') {
380
+ const sProp = (prop as symbol).toString()
381
+ if (
382
+ sProp === 'Symbol(Symbol.toPrimitive)' ||
383
+ sProp === 'Symbol(Symbol.toStringTag)' ||
384
+ sProp === 'Symbol(util.inspect.custom)'
385
+ ) {
386
+ // Node.js util.inspect
387
+ return undefined
388
+ }
389
+ }
390
+ throw new Error(
391
+ `@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Accessing the "${prop}" property of the session object is not supported. Please use getUser() instead.`
392
+ )
393
+ },
394
+ set: (_target: any, prop: string) => {
395
+ throw new Error(
396
+ `@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Setting the "${prop}" property of the session object is not supported. Please use getUser() to fetch a user object you can manipulate.`
397
+ )
398
+ },
399
+ deleteProperty: (_target: any, prop: string) => {
400
+ throw new Error(
401
+ `@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Deleting the "${prop}" property of the session object is not supported. Please use getUser() to fetch a user object you can manipulate.`
402
+ )
403
+ },
404
+ })
405
+ }
@@ -1,33 +1,5 @@
1
- import { supportsLocalStorage } from './helpers'
2
1
  import { SupportedStorage } from './types'
3
2
 
4
- /**
5
- * Provides safe access to the globalThis.localStorage property.
6
- */
7
- export const localStorageAdapter: SupportedStorage = {
8
- getItem: (key) => {
9
- if (!supportsLocalStorage()) {
10
- return null
11
- }
12
-
13
- return globalThis.localStorage.getItem(key)
14
- },
15
- setItem: (key, value) => {
16
- if (!supportsLocalStorage()) {
17
- return
18
- }
19
-
20
- globalThis.localStorage.setItem(key, value)
21
- },
22
- removeItem: (key) => {
23
- if (!supportsLocalStorage()) {
24
- return
25
- }
26
-
27
- globalThis.localStorage.removeItem(key)
28
- },
29
- }
30
-
31
3
  /**
32
4
  * Returns a localStorage-like object that stores the key-value pairs in
33
5
  * memory.
package/src/lib/types.ts CHANGED
@@ -70,6 +70,14 @@ export type GoTrueClientOptions = {
70
70
  persistSession?: boolean
71
71
  /* Provide your own local storage implementation to use instead of the browser's local storage. */
72
72
  storage?: SupportedStorage
73
+ /**
74
+ * Stores the user object in a separate storage location from the rest of the session data. When non-null, `storage` will only store a JSON object containing the access and refresh token and some adjacent metadata, while `userStorage` will only contain the user object under the key `storageKey + '-user'`.
75
+ *
76
+ * When this option is set and cookie storage is used, `getSession()` and other functions that load a session from the cookie store might not return back a user. It's very important to always use `getUser()` to fetch a user object in those scenarios.
77
+ *
78
+ * @experimental
79
+ */
80
+ userStorage?: SupportedStorage
73
81
  /* A custom fetch implementation. */
74
82
  fetch?: Fetch
75
83
  /* If set to 'pkce' PKCE flow. Defaults to the 'implicit' flow otherwise */
@@ -253,6 +261,10 @@ export interface Session {
253
261
  */
254
262
  expires_at?: number
255
263
  token_type: string
264
+
265
+ /**
266
+ * When using a separate user storage, accessing properties of this object will throw an error.
267
+ */
256
268
  user: User
257
269
  }
258
270
 
@@ -1 +1 @@
1
- export const version = '2.70.0'
1
+ export const version = '2.71.0-rc.3'