From bce56fa64dbc7f2d83ff06372c67443af99e9996 Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sat, 28 Feb 2026 23:02:43 +0100 Subject: [PATCH] feat: implement refresh token functionality and update local storage management --- lib/auth/context.tsx | 21 ++++++++++++++++++++- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/auth/context.tsx b/lib/auth/context.tsx index 14591e4..7b1920a 100644 --- a/lib/auth/context.tsx +++ b/lib/auth/context.tsx @@ -51,9 +51,25 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const router = useRouter() const pathname = usePathname() + const refreshToken = useCallback(async (): Promise => { + try { + const res = await fetch('/api/auth/refresh', { + method: 'POST', + credentials: 'include', + }) + if (res.ok) { + localStorage.setItem('ciphera_token_refreshed_at', Date.now().toString()) + } + return res.ok + } catch { + return false + } + }, []) + const login = (userData: User) => { // * We still store user profile in localStorage for optimistic UI, but NOT the token localStorage.setItem('user', JSON.stringify(userData)) + localStorage.setItem('ciphera_token_refreshed_at', Date.now().toString()) setUser(userData) router.refresh() // * Fetch full profile (including display_name) so header shows correct name without page refresh @@ -76,6 +92,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { setIsLoggingOut(true) await logoutAction() localStorage.removeItem('user') + localStorage.removeItem('ciphera_token_refreshed_at') + localStorage.removeItem('ciphera_last_activity') // * Broadcast logout to other tabs (BroadcastChannel will handle if available) if (typeof window !== 'undefined' && 'BroadcastChannel' in window) { const channel = new BroadcastChannel('ciphera_session') @@ -131,6 +149,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { if (session) { setUser(session) localStorage.setItem('user', JSON.stringify(session)) + localStorage.setItem('ciphera_token_refreshed_at', Date.now().toString()) // * Fetch full profile (including display_name) from API; preserve org_id/role from session try { const userData = await apiRequest('/auth/user/me') @@ -221,7 +240,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { {isLoggingOut && } {children} diff --git a/package-lock.json b/package-lock.json index 7cbb03b..221d1fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "pulse-frontend", "version": "0.11.1-alpha", "dependencies": { - "@ciphera-net/ui": "^0.0.77", + "@ciphera-net/ui": "^0.0.78", "@ducanh2912/next-pwa": "^10.2.9", "@radix-ui/react-icons": "^1.3.0", "@simplewebauthn/browser": "^13.2.2", @@ -1543,9 +1543,9 @@ } }, "node_modules/@ciphera-net/ui": { - "version": "0.0.77", - "resolved": "https://npm.pkg.github.com/download/@ciphera-net/ui/0.0.77/c243fce3b29ee4b3d90dd5b6b5a7d93ff3a955b9", - "integrity": "sha512-Z+aLef5843t9TIvaSV+ecC4sQi2VzX+hE6/7A/4/Y49CT91vg0x9QuQuQiV7B4j93Ui1yv+aZyWN21NY3mhKbA==", + "version": "0.0.78", + "resolved": "https://npm.pkg.github.com/download/@ciphera-net/ui/0.0.78/d012b211ddba7a83f7468ec269a842709a2409b9", + "integrity": "sha512-c9B/cZggjWnCSpICEvRBAAPgsRfjN2j3NFczQRZxRR6sZmzkwd3KzEbDfTEa2DUExBMWB4+bOgiCz+AnP2OR3g==", "dependencies": { "@radix-ui/react-icons": "^1.3.0", "clsx": "^2.1.0", diff --git a/package.json b/package.json index 34c639f..b661b14 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@ciphera-net/ui": "^0.0.77", + "@ciphera-net/ui": "^0.0.78", "@ducanh2912/next-pwa": "^10.2.9", "@radix-ui/react-icons": "^1.3.0", "@simplewebauthn/browser": "^13.2.2",