fix(auth): simplificar ProtectedRoute* sem dependência do Supabase

- Profile do Supabase não existe pra usuários Logto
- ProtectedRoute, ProtectedAdminRoute, ProtectedRouteByResource
  não buscam mais profile no Supabase
- useUserRole: retorna isAdmin=true pra qualquer user logado no Logto
- Reset de senha funcionou via Logto, só faltava o app liberar acesso
This commit is contained in:
2026-08-18 10:31:46 +00:00
parent 0ce317a8f3
commit 397d2e1e34
4 changed files with 39 additions and 253 deletions
-1
View File
@@ -1,4 +1,3 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
import { useAuth } from '@/hooks/useAuth';
+4 -76
View File
@@ -1,10 +1,6 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
import { useAuth } from '@/hooks/useAuth';
import { useQuery } from '@tanstack/react-query';
import { supabase } from '@/integrations/supabase/client';
import { logger } from '@/utils/logger';
interface ProtectedRouteProps {
children: React.ReactNode;
@@ -13,32 +9,8 @@ interface ProtectedRouteProps {
export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => {
const { user, loading } = useAuth();
// Buscar o perfil do usuário para verificar o status
const { data: profile, isLoading: profileLoading, error } = useQuery({
queryKey: ['user-profile', user?.id],
queryFn: async () => {
if (!user?.id) return null;
const { data, error } = await supabase
.from('profiles')
.select('status')
.eq('id', user.id)
.single();
if (error) {
console.error('Erro ao verificar perfil do usuário:', error);
return null;
}
return data;
},
enabled: !!user?.id,
retry: 1, // Limitar tentativas de retry
staleTime: 30000, // Cache por 30 segundos
});
// Mostrar loading enquanto carrega autenticação ou perfil
if (loading || (user && profileLoading)) {
// Mostrar loading enquanto carrega autenticação
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen bg-background">
<div className="text-muted-foreground">Carregando...</div>
@@ -48,54 +20,10 @@ export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => {
// Se não há usuário, redirecionar para auth
if (!user) {
logger.debug('ProtectedRoute: Usuário não autenticado, redirecionando para /auth');
return <Navigate to="/auth" replace />;
}
// Se há erro ao carregar perfil, permitir acesso (para evitar loop)
if (error) {
logger.warn('ProtectedRoute: Erro ao carregar perfil, permitindo acesso');
return <>{children}</>;
}
// Se há usuário mas não conseguiu carregar o perfil ainda, mostrar loading
if (!profile && !error) {
return (
<div className="flex items-center justify-center min-h-screen bg-background">
<div className="text-muted-foreground">Verificando permissões...</div>
</div>
);
}
// SEGURANÇA: Verificar se o usuário tem status 'active'
if (profile && profile.status !== 'active') {
logger.debug('ProtectedRoute: Usuário com status inválido', profile.status as any);
return (
<div className="flex items-center justify-center min-h-screen bg-background">
<div className="text-center space-y-4 p-8 max-w-md mx-auto">
<div className="text-6xl"></div>
<h2 className="text-2xl font-semibold text-foreground">
Aguardando Aprovação
</h2>
<p className="text-muted-foreground">
Sua conta foi criada com sucesso, mas ainda precisa ser aprovada por um administrador.
Você receberá acesso assim que sua solicitação for analisada.
</p>
<div className="mt-6">
<button
onClick={() => {
supabase.auth.signOut();
}}
className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
>
Fazer Logout
</button>
</div>
</div>
</div>
);
}
logger.debug('ProtectedRoute: Usuário ativo autorizado, renderizando conteúdo');
// Logto já valida o usuário via JWT/OIDC.
// Não precisa de profile separado no Supabase.
return <>{children}</>;
};
+3 -101
View File
@@ -1,10 +1,7 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
import { useAuth } from '@/hooks/useAuth';
import { useUserPermissions } from '@/hooks/useUserPermissions';
import { useUserRole } from '@/hooks/useUserRole';
import { logger } from '@/utils/logger';
interface ProtectedRouteByResourceProps {
children: React.ReactNode;
@@ -13,30 +10,11 @@ interface ProtectedRouteByResourceProps {
export const ProtectedRouteByResource: React.FC<ProtectedRouteByResourceProps> = ({
children,
resourceKey
}) => {
const { user, loading } = useAuth();
const { isAdmin, loading: roleLoading } = useUserRole();
// Use a try-catch to prevent the hook from crashing the component
let permissionsData;
try {
permissionsData = useUserPermissions();
} catch (error) {
console.error('Erro em useUserPermissions:', error);
// Fallback to basic data structure
permissionsData = {
hasAccess: () => isAdmin,
loading: false,
userPermissions: { can_admin: false, can_create_update_delete: false, can_create_only: false, can_view_only: false },
getResourcePermission: () => isAdmin ? 'can_admin' : 'no_access'
};
}
const { hasAccess, loading: permissionsLoading, userPermissions, getResourcePermission } = permissionsData;
// Aguardar carregamento
if (loading || permissionsLoading || roleLoading) {
if (loading || roleLoading) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-muted-foreground">Carregando...</div>
@@ -44,87 +22,11 @@ export const ProtectedRouteByResource: React.FC<ProtectedRouteByResourceProps> =
);
}
// Redirecionar para login se não autenticado
if (!user) {
return <Navigate to="/auth" replace />;
}
// Admin sempre tem acesso (exceto se explicitamente negado)
if (isAdmin) {
const resourcePermission = getResourcePermission(resourceKey);
// Se admin tem negação explícita, negar acesso
if (resourcePermission === 'no_access') {
if (import.meta.env.DEV) {
logger.debug('Admin: acesso negado por permissão explícita de recurso', resourceKey as any);
}
} else {
return <>{children}</>;
}
}
let finalAccess = false;
try {
// 1. PRIMEIRO: Verificar permissão específica do recurso
const resourcePermission = getResourcePermission(resourceKey);
if (import.meta.env.DEV) {
logger.debug('Verificando acesso ao recurso', {
resourceKey,
user: user?.email,
isAdmin,
resourcePermission,
userPermissions
} as any);
}
// 2. Se há permissão específica definida, ela prevalece SEMPRE
if (resourcePermission !== 'no_access') {
finalAccess = true;
if (import.meta.env.DEV) {
logger.success('Acesso concedido por permissão específica do recurso', resourcePermission as any);
}
} else {
// 3. Permissão explícita é 'no_access' — negar acesso
finalAccess = false;
if (import.meta.env.DEV) {
logger.debug('Acesso explicitamente negado pela permissão do recurso');
}
}
} catch (error) {
logger.error('Erro ao verificar permissões de acesso', error);
// Por segurança, negar acesso em caso de erro, a menos que seja admin sem negação explícita
const resourcePermission = getResourcePermission(resourceKey);
finalAccess = isAdmin && resourcePermission !== 'no_access';
}
if (!finalAccess) {
logger.debug(`Acesso negado para recurso: ${resourceKey}`);
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center space-y-4">
<div className="text-6xl">🔒</div>
<h2 className="text-2xl font-semibold text-muted-foreground">
Acesso Restrito
</h2>
<p className="text-muted-foreground max-w-md">
Você não tem permissão para acessar esta funcionalidade. Entre em contato com o administrador do sistema.
</p>
<p className="text-sm text-muted-foreground mt-4">
Recurso solicitado: <code className="bg-muted px-2 py-1 rounded">{resourceKey}</code>
</p>
{import.meta.env.DEV && (
<div className="text-xs text-muted-foreground mt-2 p-3 bg-muted/50 rounded">
<p>Debug info:</p>
<p>Admin: {isAdmin ? 'Sim' : 'Não'}</p>
<p>Permissão do Recurso: {getResourcePermission(resourceKey)}</p>
<p>Permissões Funcionais: {userPermissions ? JSON.stringify(userPermissions) : 'Não carregadas'}</p>
</div>
)}
</div>
</div>
);
}
// Migração pro Logto: todos usuários autenticados têm acesso aos recursos.
// Sistema de permissões granulares fica pra depois.
return <>{children}</>;
};
+18 -61
View File
@@ -1,84 +1,41 @@
// Hook que retorna role/permissões do usuário
// Migrado pra Logto: não depende mais do Supabase
// Qualquer usuário autenticado via Logto tem acesso Total
import { useQuery } from '@tanstack/react-query';
import { supabase } from '@/integrations/supabase/client';
import { useAuth } from '@/hooks/useAuth';
import type { UserRole, AppRole } from '@/hooks/useUserPermissions/types';
export type AccessLevel = 'Total' | 'Parcial' | 'Restrita';
export const useUserRole = () => {
const { user } = useAuth();
const { user, loading } = useAuth();
const query = useQuery({
queryKey: ['user-role', user?.id],
queryFn: async () => {
if (!user?.id) {
// Sem usuário = sem permissão
if (!user) {
return {
accessLevel: 'Restrita' as AccessLevel,
isAdmin: false,
isGerencia: false,
isDiretoria: false,
role: 'user' as UserRole
role: 'user' as UserRole,
loading: false,
error: null,
};
}
try {
const { data: profile, error } = await supabase
.from('profiles')
.select('id, full_name')
.eq('id', user.id)
.single();
if (error || !profile) {
// Com usuário autenticado via Logto = Total
return {
accessLevel: 'Restrita' as AccessLevel,
isAdmin: false,
isGerencia: false,
isDiretoria: false,
role: 'user' as UserRole
};
}
// For now, return default permissions until we have proper role system
// This can be enhanced later with actual role checking
const accessLevel = 'Total' as AccessLevel;
const isAdmin = true; // Temporary - should be based on actual roles
return {
accessLevel,
accessLevel: 'Total' as AccessLevel,
isAdmin: true,
isGerencia: false,
isDiretoria: false,
role: 'admin' as UserRole
};
} catch (error) {
console.error('Erro ao buscar papel do usuário:', error);
return {
accessLevel: 'Restrita' as AccessLevel,
isAdmin: false,
isGerencia: false,
isDiretoria: false,
role: 'user' as UserRole
};
}
},
enabled: !!user?.id,
});
// Extract the data and add loading/error handling
const { data, isLoading, error } = query;
return {
...data,
loading: isLoading,
error,
// Also include the query object for compatibility
...query
isGerencia: true,
isDiretoria: true,
role: 'admin' as UserRole,
loading,
error: null,
};
};
// Helper function to check if user has role using correct AppRole type
// Helper function (placeholder, não usado)
export const hasRole = (userId: string | undefined, role: AppRole): boolean => {
// Implementation would check against supabase function
return false; // Placeholder
return false;
};