fix: implementar PKCE flow e criar membership automaticamente para novo usuário OAuth

This commit is contained in:
Desenvolvedor
2026-02-23 18:19:28 -03:00
parent ba27c0d30e
commit 30a509546e
3 changed files with 96 additions and 23 deletions

View File

@@ -169,7 +169,7 @@ export const useAuth = () => {
const syncUserProfile = async (user: User) => {
try {
console.log('🔄 syncUserProfile: Sincronizando dados:', user.email);
// Wrapper de timeout para operações de banco
let fetchTimeoutId: ReturnType<typeof setTimeout>;
const fetchTimeout = new Promise((_, reject) => {
fetchTimeoutId = setTimeout(() => reject(new Error('Timeout syncUserProfile fetch')), 15000);
@@ -192,30 +192,95 @@ export const useAuth = () => {
return;
}
// Se não existe, criar registro na tabela usuarios
// Se não existe, criar registro completo
if (!existingUser) {
let insertTimeoutId: ReturnType<typeof setTimeout>;
const insertTimeout = new Promise((_, reject) => {
insertTimeoutId = setTimeout(() => reject(new Error('Timeout syncUserProfile insert')), 15000);
console.log('👤 Novo usuário detectado. Criando registros...');
let orgTimeoutId: ReturnType<typeof setTimeout>;
const orgTimeout = new Promise((_, reject) => {
orgTimeoutId = setTimeout(() => reject(new Error('Timeout creating org')), 15000);
});
// 1. Criar organização padrão
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const orgPromise = (supabase as any)
.from('organizacoes')
.insert({
slug: `org-${user.id.slice(0, 8)}`,
nome: `Organização de ${user.user_metadata?.full_name || user.email?.split('@')[0] || 'Usuário'}`,
status: 'ativa',
plano: 'trial'
})
.select()
.single();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { data: org, error: orgError } = await Promise.race([orgPromise, orgTimeout]) as any;
clearTimeout(orgTimeoutId!);
if (orgError) {
console.error('Erro ao criar organização:', orgError);
return;
}
console.log('✅ Organização criada:', org.id);
// 2. Criar usuário
let userTimeoutId: ReturnType<typeof setTimeout>;
const userTimeout = new Promise((_, reject) => {
userTimeoutId = setTimeout(() => reject(new Error('Timeout creating user')), 15000);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const insertPromise = (supabase as any)
const userPromise = (supabase as any)
.from('usuarios')
.insert({
id: user.id,
email: user.email!,
organizacao_id: org.id,
nome: user.user_metadata?.full_name || user.user_metadata?.nome || user.email?.split('@')[0] || 'Usuário',
ativo: true
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { error: insertError } = await Promise.race([insertPromise, insertTimeout]) as any;
clearTimeout(insertTimeoutId!);
const { error: userError } = await Promise.race([userPromise, userTimeout]) as any;
clearTimeout(userTimeoutId!);
if (insertError) {
console.error('Erro ao criar perfil do usuário:', insertError);
if (userError) {
console.error('Erro ao criar usuário:', userError);
return;
}
console.log('✅ Usuário criado');
// 3. Criar registro em organizacao_usuarios (CRÍTICO!)
let memTimeoutId: ReturnType<typeof setTimeout>;
const memTimeout = new Promise((_, reject) => {
memTimeoutId = setTimeout(() => reject(new Error('Timeout creating membership')), 15000);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const memPromise = (supabase as any)
.from('organizacao_usuarios')
.insert({
organizacao_id: org.id,
usuario_id: user.id,
role: 'owner', // Primeiro usuário é owner
ativo: true
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { error: memError } = await Promise.race([memPromise, memTimeout]) as any;
clearTimeout(memTimeoutId!);
if (memError) {
console.error('Erro ao criar membership:', memError);
return;
}
console.log('✅ Membership criado - Usuário agora tem acesso!');
} else {
console.log('✅ Usuário já existe:', existingUser.id);
}
} catch (error) {
console.error('Erro/Timeout na sincronização do perfil:', error);

View File

@@ -16,7 +16,7 @@ export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey, {
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true,
flowType: 'implicit' // Fallback para Implicit Flow evitando bugs do PKCE em domínios customizados
flowType: 'pkce' // PKCE é mais seguro e funciona melhor com domínios customizados
},
realtime: {
params: {

View File

@@ -38,18 +38,26 @@ export const AuthCallback: React.FC = () => {
clearTimeout(fallbackTimer);
setStatus('success');
// Garantir permissões do Super Admin
if (session.user.email === 'admtracksteel@gmail.com') {
console.log('👑 Super Admin detectado! Atualizando permissões...');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await (supabase.from('usuarios') as any).upsert({
id: session.user.id,
email: session.user.email,
nome: session.user.user_metadata?.full_name || 'Super Admin',
role: 'dev',
ativo: true
});
console.log('👑 Permissões de Super Admin aplicadas!');
// Sincronizar perfil do usuário ANTES de redirecionar
try {
console.log('🔄 Sincronizando perfil do usuário...');
// Importar função de sync
const { useUserStore } = await import('../stores/useUserStore');
// Aguardar sincronização com timeout
let syncTimeoutId: ReturnType<typeof setTimeout>;
const syncTimeout = new Promise((_, reject) => {
syncTimeoutId = setTimeout(() => reject(new Error('Timeout sync')), 15000);
});
const syncPromise = useUserStore.getState().fetchCurrentUser(session.user.id);
await Promise.race([syncPromise, syncTimeout]);
clearTimeout(syncTimeoutId!);
console.log('✅ Perfil sincronizado com sucesso');
} catch (err) {
console.error('⚠️ Erro ao sincronizar perfil (continuando mesmo assim):', err);
}
console.log('✅ Sessão confirmada! Redirecionando para /');