REFACTOR: Removido Mock e assegurado login com JWT

This commit is contained in:
Rafael Alves Lopes 2026-06-01 16:56:09 -03:00
parent eb6e4faf2c
commit 04971ca82e
6 changed files with 73 additions and 24 deletions

View File

@ -6,6 +6,7 @@ import {
startMicrosoftLogin, startMicrosoftLogin,
storeAuthSession, storeAuthSession,
} from '../services/authService'; } from '../services/authService';
import { isAuthenticated } from '../services/sessionService';
function decodeAuthPayload(payload) { function decodeAuthPayload(payload) {
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/'); const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
@ -21,10 +22,15 @@ export function useLogin() {
const [providers, setProviders] = useState({ ldap: true, microsoft: false }); const [providers, setProviders] = useState({ ldap: true, microsoft: false });
useEffect(() => { useEffect(() => {
if (!window.location.hash.includes('auth=') && isAuthenticated()) {
navigate('/home', { replace: true });
return;
}
getAuthConfig() getAuthConfig()
.then((config) => setProviders(config.providers || { ldap: true, microsoft: false })) .then((config) => setProviders(config.providers || { ldap: true, microsoft: false }))
.catch(() => setProviders({ ldap: true, microsoft: false })); .catch(() => setProviders({ ldap: true, microsoft: false }));
}, []); }, [navigate]);
useEffect(() => { useEffect(() => {
const hashParams = new URLSearchParams(window.location.hash.replace(/^#/, '')); const hashParams = new URLSearchParams(window.location.hash.replace(/^#/, ''));
@ -38,23 +44,13 @@ export function useLogin() {
return; return;
} catch { } catch {
window.history.replaceState({}, document.title, window.location.pathname); 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); if (window.location.search.includes('token=') || window.location.search.includes('user=')) {
const token = params.get('token');
const rawUser = params.get('user');
if (!token || !rawUser) return;
try {
const user = JSON.parse(rawUser);
storeAuthSession({ token, user });
window.history.replaceState({}, document.title, window.location.pathname); window.history.replaceState({}, document.title, window.location.pathname);
navigate('/home', { replace: true }); setError('Fluxo de login expirado. Tente novamente.');
} catch {
setError('Não foi possível concluir o login Microsoft.');
} }
}, [navigate]); }, [navigate]);

View File

@ -30,6 +30,10 @@ export function startMicrosoftLogin() {
} }
export function storeAuthSession(authResult) { 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('authToken', authResult.token);
window.sessionStorage.setItem('authUser', JSON.stringify(authResult.user)); window.sessionStorage.setItem('authUser', JSON.stringify(authResult.user));
window.localStorage.removeItem('authToken'); window.localStorage.removeItem('authToken');

View File

@ -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) { function normalizeProfile(value) {
if (!value) { if (!value) {
return null; return null;
@ -61,7 +88,14 @@ export function getCurrentUser() {
} }
export function getAuthToken() { 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() { export function isAuthenticated() {
@ -70,7 +104,7 @@ export function isAuthenticated() {
export function getCurrentUserDisplay() { export function getCurrentUserDisplay() {
const user = getCurrentUser(); 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 nameParts = fullName.split(' ').filter(Boolean);
const name = const name =
nameParts.length > 1 ? `${nameParts[0]} ${nameParts[nameParts.length - 1]}` : fullName; nameParts.length > 1 ? `${nameParts[0]} ${nameParts[nameParts.length - 1]}` : fullName;
@ -94,7 +128,7 @@ export function getCurrentUserDisplay() {
return { return {
name, name,
subtitle, subtitle,
initials: initials || 'AM', initials: initials || 'US',
}; };
} }

View File

@ -1,10 +1,14 @@
import { Navigate } from 'react-router-dom'; 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()) { if (!isAuthenticated()) {
return <Navigate to="/login" replace />; return <Navigate to="/login" replace />;
} }
if (allowedProfiles.length && !allowedProfiles.includes(getCurrentUserProfile())) {
return <Navigate to="/home" replace />;
}
return children; return children;
} }

View File

@ -9,8 +9,8 @@ import { AgentNewAttendancePage } from '../modules/attendance/pages/AgentNewAtte
import { WhatsappAdminPage } from '../modules/management/pages/WhatsappAdminPage'; import { WhatsappAdminPage } from '../modules/management/pages/WhatsappAdminPage';
import { ProtectedRoute } from './ProtectedRoute'; import { ProtectedRoute } from './ProtectedRoute';
function privatePage(page) { function privatePage(page, allowedProfiles = []) {
return <ProtectedRoute>{page}</ProtectedRoute>; return <ProtectedRoute allowedProfiles={allowedProfiles}>{page}</ProtectedRoute>;
} }
export const router = createBrowserRouter([ export const router = createBrowserRouter([
@ -48,6 +48,6 @@ export const router = createBrowserRouter([
}, },
{ {
path: '/admin/whatsapp', path: '/admin/whatsapp',
element: privatePage(<WhatsappAdminPage />), element: privatePage(<WhatsappAdminPage />, ['admin']),
}, },
]); ]);

View File

@ -1,4 +1,5 @@
import { API_BASE_URL } from './apiConfig'; import { API_BASE_URL } from './apiConfig';
import { clearSession } from '../../modules/auth/services/sessionService';
function getStoredToken() { function getStoredToken() {
return window.sessionStorage.getItem('authToken'); return window.sessionStorage.getItem('authToken');
@ -16,7 +17,7 @@ export function installAuthFetchInterceptor() {
const nativeFetch = window.fetch.bind(window); const nativeFetch = window.fetch.bind(window);
window.fetch = (resource, options = {}) => { window.fetch = async (resource, options = {}) => {
if (!shouldAttachToken(resource)) { if (!shouldAttachToken(resource)) {
return nativeFetch(resource, options); return nativeFetch(resource, options);
} }
@ -27,9 +28,19 @@ export function installAuthFetchInterceptor() {
headers.set('Authorization', `Bearer ${getStoredToken()}`); headers.set('Authorization', `Bearer ${getStoredToken()}`);
} }
return nativeFetch(resource, { const response = await nativeFetch(resource, {
...options, ...options,
headers, headers,
}); });
if (response.status === 401) {
clearSession();
if (window.location.pathname !== '/login') {
window.location.assign('/login');
}
}
return response;
}; };
} }