Files
Charis f0ff6cee8b fix(common): prevent errors when accessing local storage in certain browser setups (#36003)
fix(common): prevent errors when accessing local storage in certain
browser setups

Got many reports of errors from accessing localStorage on Safari,
probably due to certain user security settings that cause the browser to
throw an error on storage access.
2025-05-29 11:31:21 -04:00

127 lines
3.7 KiB
TypeScript

import { AuthClient, navigatorLock, User } from '@supabase/auth-js'
export const STORAGE_KEY = process.env.NEXT_PUBLIC_STORAGE_KEY || 'supabase.dashboard.auth.token'
export const AUTH_DEBUG_KEY =
process.env.NEXT_PUBLIC_AUTH_DEBUG_KEY || 'supabase.dashboard.auth.debug'
export const AUTH_DEBUG_PERSISTED_KEY =
process.env.NEXT_PUBLIC_AUTH_DEBUG_PERSISTED_KEY || 'supabase.dashboard.auth.debug.persist'
export const AUTH_NAVIGATOR_LOCK_DISABLED_KEY =
process.env.NEXT_PUBLIC_AUTH_NAVIGATOR_LOCK_KEY ||
'supabase.dashboard.auth.navigatorLock.disabled'
/**
* Catches errors thrown when accessing localStorage. Safari with certain
* security settings throws when localStorage is accessed.
*/
function safeGetLocalStorage(key: string) {
try {
return globalThis?.localStorage?.getItem(key)
} catch {
return null
}
}
const debug =
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' && safeGetLocalStorage(AUTH_DEBUG_KEY) === 'true'
const persistedDebug =
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' &&
safeGetLocalStorage(AUTH_DEBUG_PERSISTED_KEY) === 'true'
const shouldEnableNavigatorLock =
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' &&
!(safeGetLocalStorage(AUTH_NAVIGATOR_LOCK_DISABLED_KEY) === 'true')
const shouldDetectSessionInUrl = process.env.NEXT_PUBLIC_AUTH_DETECT_SESSION_IN_URL
? process.env.NEXT_PUBLIC_AUTH_DETECT_SESSION_IN_URL === 'true'
: true
const navigatorLockEnabled = !!(shouldEnableNavigatorLock && globalThis?.navigator?.locks)
if (shouldEnableNavigatorLock && !globalThis?.navigator?.locks) {
console.warn('This browser does not support the Navigator Locks API. Please update it.')
}
const tabId = Math.random().toString(16).substring(2)
let dbHandle = new Promise<IDBDatabase | null>((accept, _) => {
if (!persistedDebug) {
accept(null)
return
}
const request = indexedDB.open('auth-debug-log', 1)
request.onupgradeneeded = (event: any) => {
const db = event?.target?.result
if (!db) {
return
}
db.createObjectStore('events', { autoIncrement: true })
}
request.onsuccess = (event: any) => {
console.log('Opened persisted auth debug log IndexedDB database', tabId)
accept(event.target.result)
}
request.onerror = (event: any) => {
console.error('Failed to open persisted auth debug log IndexedDB database', event)
accept(null)
}
})
const logIndexedDB = (message: string, ...args: any[]) => {
console.log(message, ...args)
const copyArgs = structuredClone(args)
copyArgs.forEach((value) => {
if (typeof value === 'object' && value !== null) {
delete value.user
delete value.access_token
delete value.token_type
delete value.provider_token
}
})
;(async () => {
try {
const db = await dbHandle
if (!db) {
return
}
const tx = db.transaction(['events'], 'readwrite')
tx.onerror = (event: any) => {
console.error('Failed to write to persisted auth debug log IndexedDB database', event)
dbHandle = Promise.resolve(null)
}
const events = tx.objectStore('events')
events.add({
m: message.replace(/^GoTrueClient@/i, ''),
a: copyArgs,
l: window.location.pathname,
t: tabId,
})
} catch (e: any) {
console.error('Failed to log to persisted auth debug log IndexedDB database', e)
dbHandle = Promise.resolve(null)
}
})()
}
export const gotrueClient = new AuthClient({
url: process.env.NEXT_PUBLIC_GOTRUE_URL,
storageKey: STORAGE_KEY,
detectSessionInUrl: shouldDetectSessionInUrl,
debug: debug ? (persistedDebug ? logIndexedDB : true) : false,
lock: navigatorLockEnabled ? navigatorLock : undefined,
})
export type { User }