feat(auth): refresh tokens + remember me
Backend: add refresh JWT (30d, token_type claim), POST /auth/refresh endpoint (rotates token pair), remember_me on login, JWT_REFRESH_EXPIRY_DAYS env var. Extractors now reject refresh tokens on protected routes. Frontend: sessionStorage for non-remembered sessions, localStorage + refresh token for remembered sessions. Transparent 401 recovery in api.ts (retry once after refresh). Remember me checkbox on login page with security note when checked.
This commit is contained in:
@@ -15,7 +15,7 @@ import { Toaster } from "@/components/ui/sonner";
|
||||
import { ApiRequestError } from "@/lib/api";
|
||||
|
||||
function QueryProvider({ children }: { children: React.ReactNode }) {
|
||||
const { token, setToken } = useAuthContext();
|
||||
const { token, setTokens } = useAuthContext();
|
||||
const router = useRouter();
|
||||
const tokenRef = useRef(token);
|
||||
useEffect(() => { tokenRef.current = token; }, [token]);
|
||||
@@ -29,7 +29,7 @@ function QueryProvider({ children }: { children: React.ReactNode }) {
|
||||
// Guests hitting 401 on restricted content should not be redirected.
|
||||
if (error instanceof ApiRequestError && error.status === 401 && tokenRef.current) {
|
||||
toast.warning("Session expired, please log in again.");
|
||||
setToken(null);
|
||||
setTokens(null, null, false);
|
||||
router.push("/login");
|
||||
}
|
||||
},
|
||||
@@ -39,7 +39,7 @@ function QueryProvider({ children }: { children: React.ReactNode }) {
|
||||
// Mutations always require auth — redirect on 401 regardless.
|
||||
if (error instanceof ApiRequestError && error.status === 401) {
|
||||
toast.warning("Session expired, please log in again.");
|
||||
setToken(null);
|
||||
setTokens(null, null, false);
|
||||
router.push("/login");
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user