fix: implementar PKCE flow e criar membership automaticamente para novo usuário OAuth
This commit is contained in:
@@ -169,7 +169,7 @@ export const useAuth = () => {
|
|||||||
const syncUserProfile = async (user: User) => {
|
const syncUserProfile = async (user: User) => {
|
||||||
try {
|
try {
|
||||||
console.log('🔄 syncUserProfile: Sincronizando dados:', user.email);
|
console.log('🔄 syncUserProfile: Sincronizando dados:', user.email);
|
||||||
// Wrapper de timeout para operações de banco
|
|
||||||
let fetchTimeoutId: ReturnType<typeof setTimeout>;
|
let fetchTimeoutId: ReturnType<typeof setTimeout>;
|
||||||
const fetchTimeout = new Promise((_, reject) => {
|
const fetchTimeout = new Promise((_, reject) => {
|
||||||
fetchTimeoutId = setTimeout(() => reject(new Error('Timeout syncUserProfile fetch')), 15000);
|
fetchTimeoutId = setTimeout(() => reject(new Error('Timeout syncUserProfile fetch')), 15000);
|
||||||
@@ -192,30 +192,95 @@ export const useAuth = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Se não existe, criar registro na tabela usuarios
|
// Se não existe, criar registro completo
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
let insertTimeoutId: ReturnType<typeof setTimeout>;
|
console.log('👤 Novo usuário detectado. Criando registros...');
|
||||||
const insertTimeout = new Promise((_, reject) => {
|
|
||||||
insertTimeoutId = setTimeout(() => reject(new Error('Timeout syncUserProfile insert')), 15000);
|
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
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const insertPromise = (supabase as any)
|
const userPromise = (supabase as any)
|
||||||
.from('usuarios')
|
.from('usuarios')
|
||||||
.insert({
|
.insert({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
email: user.email!,
|
email: user.email!,
|
||||||
|
organizacao_id: org.id,
|
||||||
nome: user.user_metadata?.full_name || user.user_metadata?.nome || user.email?.split('@')[0] || 'Usuário',
|
nome: user.user_metadata?.full_name || user.user_metadata?.nome || user.email?.split('@')[0] || 'Usuário',
|
||||||
ativo: true
|
ativo: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const { error: insertError } = await Promise.race([insertPromise, insertTimeout]) as any;
|
const { error: userError } = await Promise.race([userPromise, userTimeout]) as any;
|
||||||
clearTimeout(insertTimeoutId!);
|
clearTimeout(userTimeoutId!);
|
||||||
|
|
||||||
if (insertError) {
|
if (userError) {
|
||||||
console.error('Erro ao criar perfil do usuário:', insertError);
|
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) {
|
} catch (error) {
|
||||||
console.error('Erro/Timeout na sincronização do perfil:', error);
|
console.error('Erro/Timeout na sincronização do perfil:', error);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey, {
|
|||||||
autoRefreshToken: true,
|
autoRefreshToken: true,
|
||||||
persistSession: true,
|
persistSession: true,
|
||||||
detectSessionInUrl: 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: {
|
realtime: {
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
@@ -38,18 +38,26 @@ export const AuthCallback: React.FC = () => {
|
|||||||
clearTimeout(fallbackTimer);
|
clearTimeout(fallbackTimer);
|
||||||
setStatus('success');
|
setStatus('success');
|
||||||
|
|
||||||
// Garantir permissões do Super Admin
|
// Sincronizar perfil do usuário ANTES de redirecionar
|
||||||
if (session.user.email === 'admtracksteel@gmail.com') {
|
try {
|
||||||
console.log('👑 Super Admin detectado! Atualizando permissões...');
|
console.log('🔄 Sincronizando perfil do usuário...');
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
await (supabase.from('usuarios') as any).upsert({
|
// Importar função de sync
|
||||||
id: session.user.id,
|
const { useUserStore } = await import('../stores/useUserStore');
|
||||||
email: session.user.email,
|
|
||||||
nome: session.user.user_metadata?.full_name || 'Super Admin',
|
// Aguardar sincronização com timeout
|
||||||
role: 'dev',
|
let syncTimeoutId: ReturnType<typeof setTimeout>;
|
||||||
ativo: true
|
const syncTimeout = new Promise((_, reject) => {
|
||||||
|
syncTimeoutId = setTimeout(() => reject(new Error('Timeout sync')), 15000);
|
||||||
});
|
});
|
||||||
console.log('👑 Permissões de Super Admin aplicadas!');
|
|
||||||
|
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 /');
|
console.log('✅ Sessão confirmada! Redirecionando para /');
|
||||||
|
|||||||
Reference in New Issue
Block a user