From 743620280fa43214689bc0c81628824b07137ac9 Mon Sep 17 00:00:00 2001 From: Anton Tranelis Date: Mon, 4 Aug 2025 22:49:40 +0200 Subject: [PATCH] fix: handle localStorage SecurityError gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add try-catch blocks around localStorage access in authLocalStorage - Return null instead of throwing when localStorage is unavailable - Prevent endless loading screen in private browsing/strict privacy modes - Add proper error logging with console.warn - Fix nullish coalescing operator usage Fixes #212 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/src/api/directus.ts | 31 ++++++++++++++++++++--------- app/src/api/userApi.ts | 7 ++++--- lib/src/Components/Auth/useAuth.tsx | 2 ++ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/src/api/directus.ts b/app/src/api/directus.ts index 7b32f8b4..13bb5f81 100644 --- a/app/src/api/directus.ts +++ b/app/src/api/directus.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable no-catch-all/no-catch-all */ +/* eslint-disable no-console */ import { createDirectus, rest, authentication } from '@directus/sdk' import type { AuthenticationData, AuthenticationStorage } from '@directus/sdk' @@ -74,24 +75,36 @@ export const authLocalStorage = (mainKey = 'directus_storage') => ({ // implementation of get, here return json parsed data from localStorage at mainKey (or null if not found) get: async () => { - const data = window.localStorage.getItem(mainKey) - if (data) { - return JSON.parse(data) + try { + const data = window.localStorage.getItem(mainKey) + if (data) { + return JSON.parse(data) + } + return null + } catch (error) { + // Handle SecurityError when localStorage is not available (e.g., in private browsing mode) + console.warn('localStorage not available:', error) + return null } - return null }, // implementation of set, here set the value at mainKey in localStorage, or remove it if value is null set: async (value: AuthenticationData | null) => { - if (!value) { - return window.localStorage.removeItem(mainKey) + try { + if (!value) { + return window.localStorage.removeItem(mainKey) + } + return window.localStorage.setItem(mainKey, JSON.stringify(value)) + } catch (error) { + // Handle SecurityError when localStorage is not available (e.g., in private browsing mode) + console.warn('localStorage not available:', error) + // Silently fail - authentication will fall back to memory-only storage } - return window.localStorage.setItem(mainKey, JSON.stringify(value)) }, }) as AuthenticationStorage export async function getRefreshToken() { const auth = await authLocalStorage().get() - return auth!.refresh_token + return auth?.refresh_token ?? null } export const directusClient = createDirectus('https://api.utopia-lab.org/') diff --git a/app/src/api/userApi.ts b/app/src/api/userApi.ts index c63e1baa..fc114b6c 100644 --- a/app/src/api/userApi.ts +++ b/app/src/api/userApi.ts @@ -4,6 +4,7 @@ /* eslint-disable no-console */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable no-catch-all/no-catch-all */ import { createUser, passwordRequest, passwordReset, readMe, updateMe } from '@directus/sdk' import { directusClient } from './directus' @@ -78,9 +79,9 @@ export class UserApi { const token = await directusClient.getToken() return token } catch (error: any) { - console.log(error) - if (error.errors[0].message) throw error.errors[0].message - else throw error + console.warn('Failed to get token:', error) + // Don't throw error - return null instead to allow graceful fallback + return null } } diff --git a/lib/src/Components/Auth/useAuth.tsx b/lib/src/Components/Auth/useAuth.tsx index 413e88f5..be8ccc28 100644 --- a/lib/src/Components/Auth/useAuth.tsx +++ b/lib/src/Components/Auth/useAuth.tsx @@ -64,10 +64,12 @@ export const AuthProvider = ({ userApi, children }: AuthProviderProps) => { setLoading(false) return me } else { + setLoading(false) return undefined } // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { + console.warn('Failed to load user token:', error) setLoading(false) return undefined } finally {