diff --git a/lib/auth-context.js b/lib/auth-context.js index ab4ee4d..ffa0553 100644 --- a/lib/auth-context.js +++ b/lib/auth-context.js @@ -146,7 +146,14 @@ export function AuthProvider({ children }) { } } } catch { - // corrupt blob -> treat as signed out + // Corrupt blob (JSON.parse failed) -> purge it so we don't re-read and + // re-fail on every cold start, then treat as signed out. Mirrors the + // expired/issuer-mismatch branch above. + try { + await SecureStore.deleteItemAsync(STORAGE_KEY); + } catch { + // best-effort cleanup; nothing more we can do here. + } } finally { if (!cancelled) setLoading(false); } diff --git a/tests/auth-context.test.js b/tests/auth-context.test.js index a056510..c571df5 100644 --- a/tests/auth-context.test.js +++ b/tests/auth-context.test.js @@ -184,6 +184,22 @@ describe('AuthProvider lifecycle', () => { expect(captured.user?.email).toBe('restored@example.com'); }); + test('purges a corrupt SecureStore blob on mount (parse failure)', async () => { + // Not valid JSON -> JSON.parse throws inside the restore effect. + mockMemStore.set(STORAGE_KEY, '{not-json'); + let captured; + render( + + (captured = c)} /> + , + ); + await waitFor(() => expect(captured.loading).toBe(false)); + expect(captured.user).toBeNull(); + // The bad blob must be deleted so the next cold start doesn't re-fail. + expect(mockSecureStore.deleteItemAsync).toHaveBeenCalledWith(STORAGE_KEY); + expect(mockMemStore.has(STORAGE_KEY)).toBe(false); + }); + test('signOut clears user + SecureStore', async () => { mockMemStore.set( STORAGE_KEY,