| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- import { AuthClient, navigatorLock, User } from '@supabase/auth-js'
- import { isBrowser } from './helpers'
- export const STORAGE_KEY = process.env.NEXT_PUBLIC_STORAGE_KEY || 'briven.dashboard.auth.token'
- export const AUTH_DEBUG_KEY =
- process.env.NEXT_PUBLIC_AUTH_DEBUG_KEY || 'briven.dashboard.auth.debug'
- export const AUTH_DEBUG_PERSISTED_KEY =
- process.env.NEXT_PUBLIC_AUTH_DEBUG_PERSISTED_KEY || 'briven.dashboard.auth.debug.persist'
- export const AUTH_NAVIGATOR_LOCK_DISABLED_KEY =
- process.env.NEXT_PUBLIC_AUTH_NAVIGATOR_LOCK_KEY ||
- 'briven.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 (isBrowser && 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)
- }
- })()
- }
- /**
- * Reference to a function that captures exceptions for debugging purposes to be sent to Sentry.
- */
- let captureException: ((e: any) => any) | null = null
- export function setCaptureException(fn: typeof captureException) {
- captureException = fn
- }
- async function debuggableNavigatorLock<R>(
- name: string,
- acquireTimeout: number,
- fn: () => Promise<R>
- ): Promise<R> {
- let stackException: any
- try {
- throw new Error('Lock is being held for over 10s here')
- } catch (e: any) {
- stackException = e
- }
- const debugTimeout = setTimeout(() => {
- ;(async () => {
- const bc = new BroadcastChannel('who-is-holding-the-lock')
- try {
- bc.postMessage({})
- } finally {
- bc.close()
- }
- console.error(
- `Waited for over 10s to acquire an Auth client lock`,
- await navigator.locks.query(),
- stackException
- )
- })()
- }, 10000)
- try {
- return await navigatorLock(name, acquireTimeout, async () => {
- clearTimeout(debugTimeout)
- const bc = new BroadcastChannel('who-is-holding-the-lock')
- bc.addEventListener('message', () => {
- console.error('Lock is held here', stackException)
- if (captureException) {
- captureException(stackException)
- }
- })
- try {
- return await fn()
- } finally {
- bc.close()
- }
- })
- } finally {
- clearTimeout(debugTimeout)
- }
- }
- 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 ? debuggableNavigatorLock : undefined,
- ...('localStorage' in globalThis
- ? { storage: globalThis.localStorage, userStorage: globalThis.localStorage }
- : null),
- })
- export type { User }
|