fix(auth): harden profile sync and fallback on non-uuid id; restore kong gateway

This commit is contained in:
2026-10-30 13:51:00 -03:00
parent 6a8290f394
commit 8ebc8dec5c
4 changed files with 98 additions and 31 deletions
+22 -5
View File
@@ -24,16 +24,33 @@ import {
// Sincroniza user Logto com profiles Supabase e retorna o UUID real do Supabase
async function syncUserToProfile(user: LogtoUser): Promise<string> {
if (!user?.sub) return user.sub;
const email = user.email || `${user.username || 'user'}@logto.local`;
const email = (user.email || `${user.username || 'user'}@logto.local`).trim();
try {
// 1. Tenta buscar o perfil existente pelo email
const { data: existing } = await supabase
// 1. Tenta buscar o perfil existente pelo email (case-insensitive)
let { data: existing, error: selectError } = await supabase
.from('profiles')
.select('id')
.eq('email', email)
.ilike('email', email)
.maybeSingle();
// Em caso de falha transitória de rede, faz 1 retry rápido antes de desistir
if (selectError) {
await new Promise((r) => setTimeout(r, 800));
const retry = await supabase
.from('profiles')
.select('id')
.ilike('email', email)
.maybeSingle();
existing = retry.data;
selectError = retry.error;
}
if (selectError) {
console.warn('Erro de rede ao consultar profile no Supabase:', selectError);
return user.sub;
}
let profileId = existing?.id;
// 2. Se existe, atualizamos o nome e retornamos o UUID
@@ -47,7 +64,7 @@ async function syncUserToProfile(user: LogtoUser): Promise<string> {
return profileId;
}
// 3. Se não existir, criamos um novo com um UUID gerado
// 3. Se confirmadamente não existir, criamos um novo com um UUID gerado
profileId = crypto.randomUUID();
const { error: insertError } = await supabase.from('profiles').insert({
id: profileId,
+13 -4
View File
@@ -11,7 +11,8 @@ export const useUserFunction = () => {
queryFn: async () => {
if (!user?.id) return null;
const { data, error } = await supabase
const isValidUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(user.id || '');
let query = supabase
.from('profiles')
.select(`
function_id,
@@ -19,9 +20,17 @@ export const useUserFunction = () => {
name,
description
)
`)
.eq('id', user.id)
.single();
`);
if (isValidUuid) {
query = query.eq('id', user.id);
} else if (user.email) {
query = query.ilike('email', user.email.trim());
} else {
query = query.eq('id', user.id);
}
const { data, error } = await query.maybeSingle();
if (error) {
console.error('Erro ao buscar função do usuário:', error);
@@ -24,26 +24,56 @@ export function useUserPermissions() {
// Load user permissions
const loadUserPermissions = useCallback(async () => {
if (!user?.id) {
if (!user?.id && !user?.email) {
setLoading(false);
return;
}
try {
console.log(`🔄 Loading permissions for user: ${user.email}`);
const isUuid = Boolean(user.id && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(user.id));
// Load functional permissions from user privilege
const { data: profile } = await supabase
// Load functional permissions from user privilege (by UUID or fallback by email)
let profileQuery = supabase
.from('profiles')
.select(`
id,
privilege_id,
privileges!inner (
name,
permissions
)
`);
if (isUuid) {
profileQuery = profileQuery.eq('id', user.id);
} else if (user.email) {
profileQuery = profileQuery.ilike('email', user.email.trim());
} else {
setLoading(false);
return;
}
let { data: profile } = await profileQuery.maybeSingle();
// Se buscou por UUID e não achou, tenta fallback por email
if (!profile && isUuid && user.email) {
const retryByEmail = await supabase
.from('profiles')
.select(`
id,
privilege_id,
privileges!inner (
name,
permissions
)
`)
.eq('id', user.id)
.single();
.ilike('email', user.email.trim())
.maybeSingle();
profile = retryByEmail.data;
}
const resolvedUserId = profile?.id || (isUuid ? user.id : null);
if (profile?.privileges?.permissions) {
// Safe type conversion with validation
@@ -61,10 +91,11 @@ export function useUserPermissions() {
}
// Load specific resource permissions
if (resolvedUserId) {
const { data: resourcePerms } = await supabase
.from('user_interface_permissions')
.select('resource_key, permission')
.eq('user_id', user.id);
.eq('user_id', resolvedUserId);
if (resourcePerms) {
const permsMap = resourcePerms.reduce((acc, perm) => {
@@ -74,13 +105,14 @@ export function useUserPermissions() {
setResourcePermissions(permsMap);
console.log('✅ Resource permissions loaded:', permsMap);
}
}
} catch (error) {
console.error('❌ Error loading permissions:', error);
} finally {
setLoading(false);
}
}, [user?.id]);
}, [user?.id, user?.email]);
useEffect(() => {
loadUserPermissions();
+13 -4
View File
@@ -30,11 +30,20 @@ export function useUserProfile() {
setLoading(true);
try {
const { data, error } = await supabase
const isValidUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(user.id || '');
let query = supabase
.from('profiles')
.select('full_name, email, profile_image_url, status')
.eq('id', user.id)
.maybeSingle();
.select('full_name, email, profile_image_url, status');
if (isValidUuid) {
query = query.eq('id', user.id);
} else if (user.email) {
query = query.ilike('email', user.email.trim());
} else {
query = query.eq('id', user.id);
}
const { data, error } = await query.maybeSingle();
if (!error && data) {
setProfile(data);