From 3cb5416251942e6f13a953b4fd2ab9a47578f71c Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Mon, 23 Feb 2026 18:57:03 +0100 Subject: [PATCH] fix: implement automatic token refresh to prevent frequent re-logins, enhancing user experience during inactivity --- CHANGELOG.md | 1 + lib/auth/context.tsx | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f27cce3..6f9a4b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Fixed - **Sign in after inactivity.** Clicking "Sign in" after a period of inactivity no longer does nothing. Previously, stale refresh cookies caused the middleware to redirect away from the login page; now only a valid access token triggers that redirect, so you can complete OAuth sign-in when your session has expired. +- **Frequent re-login.** You no longer have to sign in multiple times a day. When the access token expires after 15 minutes of inactivity, the app now automatically refreshes it using your refresh token on the next page load, so you stay logged in for up to 30 days. - **2FA disable now requires password confirmation.** Disabling 2FA sends the derived password to the backend for verification. This prevents an attacker with a hijacked session from stripping 2FA. ## [0.11.1-alpha] - 2026-02-23 diff --git a/lib/auth/context.tsx b/lib/auth/context.tsx index 537732b..a8b5810 100644 --- a/lib/auth/context.tsx +++ b/lib/auth/context.tsx @@ -110,8 +110,20 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { useEffect(() => { const init = async () => { // * 1. Check server-side session (cookies) - const session = await getSessionAction() - + let session = await getSessionAction() + + // * 2. If no access_token but refresh_token may exist, try refresh (fixes 15-min inactivity logout) + if (!session && typeof window !== 'undefined') { + const refreshRes = await fetch('/api/auth/refresh', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + credentials: 'include', + }) + if (refreshRes.ok) { + session = await getSessionAction() + } + } + if (session) { setUser(session) localStorage.setItem('user', JSON.stringify(session)) @@ -129,7 +141,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { localStorage.removeItem('user') setUser(null) } - + // * Clear legacy tokens if they exist (migration) if (localStorage.getItem('token')) { localStorage.removeItem('token')