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,
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]);

View File

@ -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');

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) {
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',
};
}

View File

@ -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 <Navigate to="/login" replace />;
}
if (allowedProfiles.length && !allowedProfiles.includes(getCurrentUserProfile())) {
return <Navigate to="/home" replace />;
}
return children;
}

View File

@ -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 <ProtectedRoute>{page}</ProtectedRoute>;
function privatePage(page, allowedProfiles = []) {
return <ProtectedRoute allowedProfiles={allowedProfiles}>{page}</ProtectedRoute>;
}
export const router = createBrowserRouter([
@ -48,6 +48,6 @@ export const router = createBrowserRouter([
},
{
path: '/admin/whatsapp',
element: privatePage(<WhatsappAdminPage />),
element: privatePage(<WhatsappAdminPage />, ['admin']),
},
]);

View File

@ -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;
};
}