From 10c67672f83d16427e9f7202346873ef253633b2 Mon Sep 17 00:00:00 2001 From: Marcos Reifonas Date: Wed, 19 Aug 2026 17:51:13 +0000 Subject: [PATCH] fix(auth): Block access to protected routes for users without 'active' status in supabase --- src/components/ProtectedAdminRoute.tsx | 26 ++++++++++++++++++--- src/components/ProtectedRoute.tsx | 19 ++++++++++++--- src/components/ProtectedRouteByResource.tsx | 20 ++++++++++++++-- src/hooks/useUserProfile.tsx | 10 +++++--- 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/src/components/ProtectedAdminRoute.tsx b/src/components/ProtectedAdminRoute.tsx index dfabf72..499ad72 100644 --- a/src/components/ProtectedAdminRoute.tsx +++ b/src/components/ProtectedAdminRoute.tsx @@ -3,22 +3,42 @@ import { Navigate } from 'react-router-dom'; import { useAuth } from '@/hooks/useAuth'; import { useUserRole } from '@/hooks/useUserRole'; +import { useUserProfile } from '@/hooks/useUserProfile'; + interface ProtectedAdminRouteProps { children: React.ReactNode; } export const ProtectedAdminRoute: React.FC = ({ children }) => { - const { user, loading } = useAuth(); + const { user, loading: authLoading } = useAuth(); const { isAdmin, loading: roleLoading } = useUserRole(); + const { profile, loading: profileLoading } = useUserProfile(); - if (loading || roleLoading) { - return
Carregando...
; + const loading = authLoading || roleLoading || profileLoading; + + if (loading) { + return ( +
+
Carregando...
+
+ ); } if (!user) { return ; } + // Verificar se o usuário está ativo no sistema + if (profile && profile.status !== 'active') { + return ( +
+

Acesso Bloqueado

+

Seu usuário existe, mas encontra-se com status: {profile.status || 'pendente'}.

+

Por favor, contate o administrador para aprovar o seu acesso.

+
+ ); + } + if (!isAdmin) { return ; } diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx index 8940af6..14f96fe 100644 --- a/src/components/ProtectedRoute.tsx +++ b/src/components/ProtectedRoute.tsx @@ -1,13 +1,17 @@ import React from 'react'; import { Navigate } from 'react-router-dom'; import { useAuth } from '@/hooks/useAuth'; +import { useUserProfile } from '@/hooks/useUserProfile'; interface ProtectedRouteProps { children: React.ReactNode; } export const ProtectedRoute: React.FC = ({ children }) => { - const { user, loading } = useAuth(); + const { user, loading: authLoading } = useAuth(); + const { profile, loading: profileLoading } = useUserProfile(); + + const loading = authLoading || profileLoading; // Mostrar loading enquanto carrega autenticação if (loading) { @@ -23,7 +27,16 @@ export const ProtectedRoute: React.FC = ({ children }) => { return ; } - // Logto já valida o usuário via JWT/OIDC. - // Não precisa de profile separado no Supabase. + // Verificar se o usuário está ativo no sistema + if (profile && profile.status !== 'active') { + return ( +
+

Acesso Bloqueado

+

Seu usuário existe, mas encontra-se com status: {profile.status || 'pendente'}.

+

Por favor, contate o administrador para aprovar o seu acesso.

+
+ ); + } + return <>{children}; }; \ No newline at end of file diff --git a/src/components/ProtectedRouteByResource.tsx b/src/components/ProtectedRouteByResource.tsx index 46f63e2..50ffbe6 100644 --- a/src/components/ProtectedRouteByResource.tsx +++ b/src/components/ProtectedRouteByResource.tsx @@ -3,6 +3,8 @@ import { Navigate } from 'react-router-dom'; import { useAuth } from '@/hooks/useAuth'; import { useUserRole } from '@/hooks/useUserRole'; +import { useUserProfile } from '@/hooks/useUserProfile'; + interface ProtectedRouteByResourceProps { children: React.ReactNode; resourceKey: string; @@ -11,10 +13,13 @@ interface ProtectedRouteByResourceProps { export const ProtectedRouteByResource: React.FC = ({ children, }) => { - const { user, loading } = useAuth(); + const { user, loading: authLoading } = useAuth(); const { isAdmin, loading: roleLoading } = useUserRole(); + const { profile, loading: profileLoading } = useUserProfile(); - if (loading || roleLoading) { + const loading = authLoading || roleLoading || profileLoading; + + if (loading) { return (
Carregando...
@@ -26,6 +31,17 @@ export const ProtectedRouteByResource: React.FC = return ; } + // Verificar se o usuário está ativo no sistema + if (profile && profile.status !== 'active') { + return ( +
+

Acesso Bloqueado

+

Seu usuário existe, mas encontra-se com status: {profile.status || 'pendente'}.

+

Por favor, contate o administrador para aprovar o seu acesso.

+
+ ); + } + // Migração pro Logto: todos usuários autenticados têm acesso aos recursos. // Sistema de permissões granulares fica pra depois. return <>{children}; diff --git a/src/hooks/useUserProfile.tsx b/src/hooks/useUserProfile.tsx index 5901266..0867e1d 100644 --- a/src/hooks/useUserProfile.tsx +++ b/src/hooks/useUserProfile.tsx @@ -7,6 +7,7 @@ interface UserProfile { full_name: string | null; email: string | null; profile_image_url: string | null; + status: string | null; } export function useUserProfile() { @@ -21,6 +22,7 @@ export function useUserProfile() { setProfile(null); setLoading(false); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [user]); const fetchUserProfile = async () => { @@ -30,7 +32,7 @@ export function useUserProfile() { try { const { data, error } = await supabase .from('profiles') - .select('full_name, email, profile_image_url') + .select('full_name, email, profile_image_url, status') .eq('id', user.id) .maybeSingle(); @@ -41,7 +43,8 @@ export function useUserProfile() { setProfile({ full_name: user.user_metadata?.full_name || null, email: user.email || null, - profile_image_url: null + profile_image_url: null, + status: null }); } } catch (error) { @@ -50,7 +53,8 @@ export function useUserProfile() { setProfile({ full_name: user.user_metadata?.full_name || null, email: user.email || null, - profile_image_url: null + profile_image_url: null, + status: null }); } finally { setLoading(false);