Enhance authentication handling
This commit is contained in:
@@ -124,6 +124,11 @@ app.get('/me', async (req, res) => {
|
|||||||
|
|
||||||
const user = userResult.rows[0];
|
const user = userResult.rows[0];
|
||||||
|
|
||||||
|
// Check if user is active
|
||||||
|
if (!user.is_active) {
|
||||||
|
return res.status(403).json({ error: 'Account is inactive' });
|
||||||
|
}
|
||||||
|
|
||||||
// Get user permissions
|
// Get user permissions
|
||||||
let permissions = [];
|
let permissions = [];
|
||||||
if (!user.is_admin) {
|
if (!user.is_admin) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
import { createContext, useState, useEffect, useRef, ReactNode, useCallback } from 'react';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
|
|
||||||
export interface Permission {
|
export interface Permission {
|
||||||
@@ -61,7 +61,10 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
throw new Error(errorData.error || 'Failed to fetch user data');
|
console.error('Auth check failed:', response.status, errorData);
|
||||||
|
// Any failed /me response means the session is invalid — logout
|
||||||
|
logout();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userData = await response.json();
|
const userData = await response.json();
|
||||||
@@ -76,14 +79,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
const errorMessage = err instanceof Error ? err.message : 'An unknown error occurred';
|
const errorMessage = err instanceof Error ? err.message : 'An unknown error occurred';
|
||||||
setError(errorMessage);
|
setError(errorMessage);
|
||||||
console.error('Auth error:', errorMessage);
|
console.error('Auth error:', errorMessage);
|
||||||
|
// Network errors (server down, etc.) — don't logout, just show error
|
||||||
// Clear token if authentication failed
|
|
||||||
if (err instanceof Error &&
|
|
||||||
(err.message.includes('authentication') ||
|
|
||||||
err.message.includes('token') ||
|
|
||||||
err.message.includes('401'))) {
|
|
||||||
logout();
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@@ -99,6 +95,30 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
}, [token, fetchCurrentUser]);
|
}, [token, fetchCurrentUser]);
|
||||||
|
|
||||||
|
// Re-validate auth when user returns to the tab after being away
|
||||||
|
const lastCheckRef = useRef(Date.now());
|
||||||
|
useEffect(() => {
|
||||||
|
const onVisibilityChange = () => {
|
||||||
|
if (document.visibilityState === 'visible' && token) {
|
||||||
|
const elapsed = Date.now() - lastCheckRef.current;
|
||||||
|
// Only re-check if at least 5 minutes have passed
|
||||||
|
if (elapsed > 5 * 60 * 1000) {
|
||||||
|
lastCheckRef.current = Date.now();
|
||||||
|
fetchCurrentUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('visibilitychange', onVisibilityChange);
|
||||||
|
return () => document.removeEventListener('visibilitychange', onVisibilityChange);
|
||||||
|
}, [token, fetchCurrentUser]);
|
||||||
|
|
||||||
|
// Listen for auth:logout events from anywhere in the app (e.g. failed API calls)
|
||||||
|
useEffect(() => {
|
||||||
|
const onForceLogout = () => logout();
|
||||||
|
window.addEventListener('auth:logout', onForceLogout);
|
||||||
|
return () => window.removeEventListener('auth:logout', onForceLogout);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const login = async (username: string, password: string) => {
|
const login = async (username: string, password: string) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user