fix(auth): Block access to protected routes for users without 'active' status in supabase

This commit is contained in:
2026-08-19 17:51:13 +00:00
parent 2086c63030
commit 10c67672f8
4 changed files with 64 additions and 11 deletions
+23 -3
View File
@@ -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<ProtectedAdminRouteProps> = ({ children }) => {
const { user, loading } = useAuth();
const { user, loading: authLoading } = useAuth();
const { isAdmin, loading: roleLoading } = useUserRole();
const { profile, loading: profileLoading } = useUserProfile();
if (loading || roleLoading) {
return <div>Carregando...</div>;
const loading = authLoading || roleLoading || profileLoading;
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen bg-background">
<div className="text-muted-foreground">Carregando...</div>
</div>
);
}
if (!user) {
return <Navigate to="/auth" replace />;
}
// Verificar se o usuário está ativo no sistema
if (profile && profile.status !== 'active') {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-background text-center p-4">
<h1 className="text-2xl font-bold mb-4">Acesso Bloqueado</h1>
<p className="text-muted-foreground mb-2">Seu usuário existe, mas encontra-se com status: <strong>{profile.status || 'pendente'}</strong>.</p>
<p className="text-muted-foreground">Por favor, contate o administrador para aprovar o seu acesso.</p>
</div>
);
}
if (!isAdmin) {
return <Navigate to="/dashboard" replace />;
}
+16 -3
View File
@@ -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<ProtectedRouteProps> = ({ 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<ProtectedRouteProps> = ({ children }) => {
return <Navigate to="/auth" replace />;
}
// 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 (
<div className="flex flex-col items-center justify-center min-h-screen bg-background text-center p-4">
<h1 className="text-2xl font-bold mb-4">Acesso Bloqueado</h1>
<p className="text-muted-foreground mb-2">Seu usuário existe, mas encontra-se com status: <strong>{profile.status || 'pendente'}</strong>.</p>
<p className="text-muted-foreground">Por favor, contate o administrador para aprovar o seu acesso.</p>
</div>
);
}
return <>{children}</>;
};
+18 -2
View File
@@ -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<ProtectedRouteByResourceProps> = ({
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 (
<div className="flex items-center justify-center min-h-screen">
<div className="text-muted-foreground">Carregando...</div>
@@ -26,6 +31,17 @@ export const ProtectedRouteByResource: React.FC<ProtectedRouteByResourceProps> =
return <Navigate to="/auth" replace />;
}
// Verificar se o usuário está ativo no sistema
if (profile && profile.status !== 'active') {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-background text-center p-4">
<h1 className="text-2xl font-bold mb-4">Acesso Bloqueado</h1>
<p className="text-muted-foreground mb-2">Seu usuário existe, mas encontra-se com status: <strong>{profile.status || 'pendente'}</strong>.</p>
<p className="text-muted-foreground">Por favor, contate o administrador para aprovar o seu acesso.</p>
</div>
);
}
// Migração pro Logto: todos usuários autenticados têm acesso aos recursos.
// Sistema de permissões granulares fica pra depois.
return <>{children}</>;
+7 -3
View File
@@ -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);