diff --git a/src/modules/auth/hooks/useLogin.js b/src/modules/auth/hooks/useLogin.js
index ef21588..1cec748 100644
--- a/src/modules/auth/hooks/useLogin.js
+++ b/src/modules/auth/hooks/useLogin.js
@@ -6,6 +6,7 @@ import {
startMicrosoftLogin,
storeAuthSession,
} from '../services/authService';
+import { isAuthenticated } from '../services/sessionService';
function decodeAuthPayload(payload) {
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
@@ -21,10 +22,15 @@ export function useLogin() {
const [providers, setProviders] = useState({ ldap: true, microsoft: false });
useEffect(() => {
+ if (!window.location.hash.includes('auth=') && isAuthenticated()) {
+ navigate('/home', { replace: true });
+ return;
+ }
+
getAuthConfig()
.then((config) => setProviders(config.providers || { ldap: true, microsoft: false }))
.catch(() => setProviders({ ldap: true, microsoft: false }));
- }, []);
+ }, [navigate]);
useEffect(() => {
const hashParams = new URLSearchParams(window.location.hash.replace(/^#/, ''));
@@ -38,23 +44,13 @@ export function useLogin() {
return;
} catch {
window.history.replaceState({}, document.title, window.location.pathname);
- setError('Não foi possível concluir o login Microsoft.');
+ setError('Nao foi possivel concluir o login Microsoft.');
}
}
- const params = new URLSearchParams(window.location.search);
- const token = params.get('token');
- const rawUser = params.get('user');
-
- if (!token || !rawUser) return;
-
- try {
- const user = JSON.parse(rawUser);
- storeAuthSession({ token, user });
+ if (window.location.search.includes('token=') || window.location.search.includes('user=')) {
window.history.replaceState({}, document.title, window.location.pathname);
- navigate('/home', { replace: true });
- } catch {
- setError('Não foi possível concluir o login Microsoft.');
+ setError('Fluxo de login expirado. Tente novamente.');
}
}, [navigate]);
diff --git a/src/modules/auth/services/authService.js b/src/modules/auth/services/authService.js
index 8ad5a7b..b539d3f 100644
--- a/src/modules/auth/services/authService.js
+++ b/src/modules/auth/services/authService.js
@@ -30,6 +30,10 @@ export function startMicrosoftLogin() {
}
export function storeAuthSession(authResult) {
+ if (!authResult?.token || !authResult?.user) {
+ throw new Error('Sessao de autenticacao invalida.');
+ }
+
window.sessionStorage.setItem('authToken', authResult.token);
window.sessionStorage.setItem('authUser', JSON.stringify(authResult.user));
window.localStorage.removeItem('authToken');
diff --git a/src/modules/auth/services/sessionService.js b/src/modules/auth/services/sessionService.js
index 2e04998..3d251e7 100644
--- a/src/modules/auth/services/sessionService.js
+++ b/src/modules/auth/services/sessionService.js
@@ -22,6 +22,33 @@ function readStoredUser() {
}
}
+function decodeJwtPayload(token) {
+ const [, payload] = String(token || '').split('.');
+
+ if (!payload) {
+ return null;
+ }
+
+ try {
+ const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
+ const paddedBase64 = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');
+
+ return JSON.parse(window.atob(paddedBase64));
+ } catch {
+ return null;
+ }
+}
+
+function isTokenExpired(token) {
+ const payload = decodeJwtPayload(token);
+
+ if (!payload?.exp) {
+ return true;
+ }
+
+ return payload.exp * 1000 <= Date.now();
+}
+
function normalizeProfile(value) {
if (!value) {
return null;
@@ -61,7 +88,14 @@ export function getCurrentUser() {
}
export function getAuthToken() {
- return window.sessionStorage.getItem('authToken');
+ const token = window.sessionStorage.getItem('authToken');
+
+ if (!token || isTokenExpired(token)) {
+ clearSession();
+ return null;
+ }
+
+ return token;
}
export function isAuthenticated() {
@@ -70,7 +104,7 @@ export function isAuthenticated() {
export function getCurrentUserDisplay() {
const user = getCurrentUser();
- const fullName = user?.name || user?.nome || user?.username || 'Ana Camolesi';
+ const fullName = user?.name || user?.nome || user?.username || user?.email || 'Usuario';
const nameParts = fullName.split(' ').filter(Boolean);
const name =
nameParts.length > 1 ? `${nameParts[0]} ${nameParts[nameParts.length - 1]}` : fullName;
@@ -94,7 +128,7 @@ export function getCurrentUserDisplay() {
return {
name,
subtitle,
- initials: initials || 'AM',
+ initials: initials || 'US',
};
}
diff --git a/src/routes/ProtectedRoute.jsx b/src/routes/ProtectedRoute.jsx
index 4043c8d..cd0a586 100644
--- a/src/routes/ProtectedRoute.jsx
+++ b/src/routes/ProtectedRoute.jsx
@@ -1,10 +1,14 @@
import { Navigate } from 'react-router-dom';
-import { isAuthenticated } from '../modules/auth/services/sessionService';
+import { getCurrentUserProfile, isAuthenticated } from '../modules/auth/services/sessionService';
-export function ProtectedRoute({ children }) {
+export function ProtectedRoute({ children, allowedProfiles = [] }) {
if (!isAuthenticated()) {
return ;
}
+ if (allowedProfiles.length && !allowedProfiles.includes(getCurrentUserProfile())) {
+ return ;
+ }
+
return children;
}
diff --git a/src/routes/router.jsx b/src/routes/router.jsx
index 8a9efda..9032af2 100644
--- a/src/routes/router.jsx
+++ b/src/routes/router.jsx
@@ -9,8 +9,8 @@ import { AgentNewAttendancePage } from '../modules/attendance/pages/AgentNewAtte
import { WhatsappAdminPage } from '../modules/management/pages/WhatsappAdminPage';
import { ProtectedRoute } from './ProtectedRoute';
-function privatePage(page) {
- return {page};
+function privatePage(page, allowedProfiles = []) {
+ return {page};
}
export const router = createBrowserRouter([
@@ -48,6 +48,6 @@ export const router = createBrowserRouter([
},
{
path: '/admin/whatsapp',
- element: privatePage(),
+ element: privatePage(, ['admin']),
},
]);
diff --git a/src/shared/services/authFetch.js b/src/shared/services/authFetch.js
index fb1e149..512709d 100644
--- a/src/shared/services/authFetch.js
+++ b/src/shared/services/authFetch.js
@@ -1,4 +1,5 @@
import { API_BASE_URL } from './apiConfig';
+import { clearSession } from '../../modules/auth/services/sessionService';
function getStoredToken() {
return window.sessionStorage.getItem('authToken');
@@ -16,7 +17,7 @@ export function installAuthFetchInterceptor() {
const nativeFetch = window.fetch.bind(window);
- window.fetch = (resource, options = {}) => {
+ window.fetch = async (resource, options = {}) => {
if (!shouldAttachToken(resource)) {
return nativeFetch(resource, options);
}
@@ -27,9 +28,19 @@ export function installAuthFetchInterceptor() {
headers.set('Authorization', `Bearer ${getStoredToken()}`);
}
- return nativeFetch(resource, {
+ const response = await nativeFetch(resource, {
...options,
headers,
});
+
+ if (response.status === 401) {
+ clearSession();
+
+ if (window.location.pathname !== '/login') {
+ window.location.assign('/login');
+ }
+ }
+
+ return response;
};
}