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:
@@ -16,14 +16,21 @@ export function useCurrentUser() {
|
||||
}
|
||||
|
||||
export function useLogin() {
|
||||
const { setToken } = useAuthContext();
|
||||
const { setTokens } = useAuthContext();
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ email, password }: { email: string; password: string }) =>
|
||||
api.auth.login(email, password),
|
||||
onSuccess: (data) => {
|
||||
setToken(data.access_token);
|
||||
mutationFn: ({
|
||||
email,
|
||||
password,
|
||||
rememberMe,
|
||||
}: {
|
||||
email: string;
|
||||
password: string;
|
||||
rememberMe: boolean;
|
||||
}) => api.auth.login(email, password, rememberMe),
|
||||
onSuccess: (data, { rememberMe }) => {
|
||||
setTokens(data.access_token, data.refresh_token ?? null, rememberMe);
|
||||
queryClient.invalidateQueries({ queryKey: ["me"] });
|
||||
router.push("/dashboard");
|
||||
},
|
||||
@@ -31,14 +38,14 @@ export function useLogin() {
|
||||
}
|
||||
|
||||
export function useRegister() {
|
||||
const { setToken } = useAuthContext();
|
||||
const { setTokens } = useAuthContext();
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ email, password }: { email: string; password: string }) =>
|
||||
api.auth.register(email, password),
|
||||
onSuccess: (data) => {
|
||||
setToken(data.access_token);
|
||||
setTokens(data.access_token, null, false);
|
||||
queryClient.invalidateQueries({ queryKey: ["me"] });
|
||||
router.push("/dashboard");
|
||||
},
|
||||
@@ -46,13 +53,13 @@ export function useRegister() {
|
||||
}
|
||||
|
||||
export function useLogout() {
|
||||
const { token, setToken } = useAuthContext();
|
||||
const { token, setTokens } = useAuthContext();
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: () => (token ? api.auth.logout(token) : Promise.resolve()),
|
||||
onSettled: () => {
|
||||
setToken(null);
|
||||
setTokens(null, null, false);
|
||||
queryClient.clear();
|
||||
router.push("/login");
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user