feat(auth): separate doctor/patient roles and correct mapping

separate flow: doctors → user_roles

add list/update user_roles

map patient->user, doctor->doctor

 adjust list/update patient_assignments

extend verifyLoginPermission

correct 22P02 and FK 23503; basic tests performed
This commit is contained in:
M-Gabrielly 2025-10-09 12:39:34 -03:00
parent 7819eb2fdf
commit c48efe8336
4 changed files with 293 additions and 143 deletions

View File

@ -46,7 +46,7 @@ import {
buscarMedicoPorId,
Medico,
listarAutorizacoesUsuario,
atualizarAutorizacoesUsuario,
atualizarUserRoles,
buscarUsuarioPorEmail,
criarUsuarioMedico,
type AuthorizationRole,
@ -425,12 +425,14 @@ export default function DoutoresPage() {
if (selection.paciente) selectedRoles.push("paciente");
if (selection.medico) selectedRoles.push("medico");
console.log("[Auth] Atualizando roles:", selectedRoles, "para user_id:", userId, "doctor_id:", authTargetDoctor.id);
const result = await atualizarAutorizacoesUsuario(
console.log(
"[Auth] Atualizando roles (user_roles) :",
selectedRoles,
"para user_id:",
userId,
authTargetDoctor.id, // doctor_id (usamos como patient_id na tabela)
selectedRoles
);
// Para médicos vamos gravar roles globais na tabela `user_roles` (sem patient_id)
const result = await atualizarUserRoles(userId, selectedRoles);
console.log("[Auth] Resultado:", result);
toast({

View File

@ -75,14 +75,6 @@ export function CredentialsDialog({
</AlertDescription>
</Alert>
<Alert className="bg-blue-50 border-blue-200">
<AlertDescription className="text-blue-900">
<strong>📧 Confirme o email:</strong> Um email de confirmação foi
enviado para <strong>{email}</strong>. O {userType} deve clicar no
link de confirmação antes de fazer o primeiro login.
</AlertDescription>
</Alert>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="email">Email de Acesso</Label>
@ -147,33 +139,6 @@ export function CredentialsDialog({
</div>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-md p-3 text-sm text-blue-900">
<strong>Próximos passos:</strong>
<ol className="list-decimal list-inside mt-2 space-y-1">
<li>Compartilhe estas credenciais com o {userType}</li>
<li>
<strong className="text-blue-700">
O {userType} deve confirmar o email
</strong>{" "}
clicando no link enviado para <strong>{email}</strong> (verifique
também a pasta de spam)
</li>
<li>
Após confirmar o email, o {userType} deve acessar:{" "}
<code className="bg-blue-100 px-1 py-0.5 rounded text-xs font-mono">
{userType === "médico" ? "/login" : "/login-paciente"}
</code>
</li>
<li>
Após o login, terá acesso à área:{" "}
<code className="bg-blue-100 px-1 py-0.5 rounded text-xs font-mono">
{userType === "médico" ? "/profissional" : "/paciente"}
</code>
</li>
<li>Recomende trocar a senha no primeiro acesso</li>
</ol>
</div>
<DialogFooter className="flex-col sm:flex-row gap-2">
<Button
type="button"

View File

@ -480,8 +480,8 @@ export async function buscarMedicoPorId(id: string | number): Promise<Medico> {
const res = await fetch(url, { method: "GET", headers: baseHeaders() });
const array = await parse<Medico[]>(res);
if (array && array.length > 0) {
console.log("Médico encontrado no Supabase:", array[0]);
console.log("🔍 Campo especialidade no médico:", {
console.log("Médico encontrado no Supabase:", array[0]);
console.log("Campo especialidade no médico:", {
especialidade: array[0].especialidade,
specialty: (array[0] as any).specialty,
hasEspecialidade: !!array[0].especialidade,
@ -490,7 +490,7 @@ export async function buscarMedicoPorId(id: string | number): Promise<Medico> {
return array[0];
}
} catch (error) {
console.warn("⚠️ Erro ao buscar no Supabase, tentando mock API:", error);
console.warn("Erro ao buscar no Supabase, tentando mock API:", error);
}
// Se não encontrar no Supabase, tenta o mock API
@ -511,10 +511,10 @@ export async function buscarMedicoPorId(id: string | number): Promise<Medico> {
}
const medico = await res.json();
console.log("Médico encontrado no Mock API:", medico);
console.log("Médico encontrado no Mock API:", medico);
return medico as Medico;
} catch (error) {
console.error("Erro ao buscar médico em ambas as APIs:", error);
console.error("Erro ao buscar médico em ambas as APIs:", error);
throw new Error("404: Médico não encontrado");
}
}
@ -541,8 +541,8 @@ export async function atualizarMedico(
id: string | number,
input: MedicoInput,
): Promise<Medico> {
console.log(`🔄 Tentando atualizar médico ID: ${id}`);
console.log(`📤 Payload original:`, input);
console.log(` Tentando atualizar médico ID: ${id}`);
console.log(` Payload original:`, input);
// Criar um payload limpo apenas com campos básicos que sabemos que existem
const cleanPayload = {
@ -560,12 +560,12 @@ export async function atualizarMedico(
active: input.active ?? true,
};
console.log(`📤 Payload limpo:`, cleanPayload);
console.log(` Payload limpo:`, cleanPayload);
// Atualizar apenas no Supabase (dados reais)
try {
const url = `${REST}/doctors?id=eq.${id}`;
console.log(`🌐 URL de atualização: ${url}`);
console.log(` URL de atualização: ${url}`);
const res = await fetch(url, {
method: "PATCH",
@ -576,17 +576,17 @@ export async function atualizarMedico(
body: JSON.stringify(cleanPayload),
});
console.log(`📡 Resposta do servidor: ${res.status} ${res.statusText}`);
console.log(` Resposta do servidor: ${res.status} ${res.statusText}`);
if (res.ok) {
const array = await parse<Medico[] | Medico>(res);
const result = Array.isArray(array) ? array[0] : (array as Medico);
console.log(" Médico atualizado no Supabase:", result);
console.log(" Médico atualizado no Supabase:", result);
return result;
} else {
// Vamos tentar ver o erro detalhado
const errorText = await res.text();
console.error(` Erro detalhado do Supabase:`, {
console.error(` Erro detalhado do Supabase:`, {
status: res.status,
statusText: res.statusText,
response: errorText,
@ -597,7 +597,7 @@ export async function atualizarMedico(
);
}
} catch (error) {
console.error(" Erro ao atualizar médico:", error);
console.error(" Erro ao atualizar médico:", error);
throw error;
}
}
@ -610,22 +610,26 @@ export async function excluirMedico(id: string | number): Promise<void> {
// ===== USUÁRIOS =====
export type AuthorizationRole = "paciente" | "medico";
type ServerAuthorizationRole = "patient" | "doctor";
// IMPORTANTE: O ENUM app_role no Supabase:
// - user_roles: aceita "medico" (confirmado funcionando)
// - patient_assignments: provavelmente aceita "user" para pacientes (baseado na API /functions/v1/create-user)
// Valores aceitos pelo ENUM: admin | gestor | medico | secretaria | user
type ServerAuthorizationRole = "medico" | "user";
const AUTHORIZATION_ROLE_TO_SERVER: Record<
AuthorizationRole,
ServerAuthorizationRole
> = {
paciente: "patient",
medico: "doctor",
paciente: "user", // Mapeia "paciente" interno para "user" no servidor
medico: "medico",
};
const SERVER_ROLE_TO_AUTHORIZATION: Record<
ServerAuthorizationRole,
AuthorizationRole
> = {
patient: "paciente",
doctor: "medico",
user: "paciente", // Mapeia "user" do servidor para "paciente" interno
medico: "medico",
};
export type UserRole = {
@ -644,6 +648,66 @@ export async function listarUserRoles(): Promise<UserRole[]> {
return await parse<UserRole[]>(res);
}
/**
* Lista roles globais do usuário (tabela user_roles) e mapeia para AuthorizationRole
*/
export async function listarUserRolesDoUsuario(
userId: string,
): Promise<AuthorizationRole[]> {
const url = `${REST}/user_roles?user_id=eq.${userId}`;
const res = await fetch(url, { method: "GET", headers: baseHeaders() });
const rows = await parse<Array<{ role: string }>>(res);
if (!Array.isArray(rows) || rows.length === 0) return [];
// O banco usa "medico" e "paciente" diretamente
return rows
.map((r) => r.role as AuthorizationRole)
.filter((v, i, a) => a.indexOf(v) === i);
}
/**
* Atualiza roles globais do usuário (user_roles): remove antigas e cria novas.
*/
export async function atualizarUserRoles(
userId: string,
roles: AuthorizationRole[],
): Promise<AuthorizationRole[]> {
// Remove roles antigas
const deleteUrl = `${REST}/user_roles?user_id=eq.${userId}`;
const deleteRes = await fetch(deleteUrl, {
method: "DELETE",
headers: baseHeaders(),
});
if (!deleteRes.ok) {
throw new Error("Não foi possível remover roles anteriores (user_roles)");
}
if (roles.length === 0) return [];
// Mapeia para nome de role no servidor
const payload = roles.map((r) => ({
user_id: userId,
role: AUTHORIZATION_ROLE_TO_SERVER[r],
}));
console.log("[atualizarUserRoles] Payload:", payload);
const res = await fetch(`${REST}/user_roles`, {
method: "POST",
headers: withPrefer(
{ ...baseHeaders(), "Content-Type": "application/json" },
"return=representation",
),
body: JSON.stringify(payload),
});
const data = await parse<Array<{ role: string }>>(res);
if (!Array.isArray(data)) return [];
// O banco retorna "medico" e "paciente" diretamente
return data
.map((r) => r.role as AuthorizationRole)
.filter((v, i, a) => a.indexOf(v) === i);
}
function buildRoleFilterParameter(roles: ServerAuthorizationRole[]): string {
const encoded = roles
.map((role) => encodeURIComponent(`"${role}"`))
@ -657,18 +721,90 @@ function buildRoleFilterParameter(roles: ServerAuthorizationRole[]): string {
export async function listarAutorizacoesUsuario(
userId: string,
): Promise<AuthorizationRole[]> {
console.log("[listarAutorizacoesUsuario] Buscando roles para user_id:", userId);
const url = `${REST}/patient_assignments?user_id=eq.${userId}`;
const res = await fetch(url, { method: "GET", headers: baseHeaders() });
const rows = await parse<Array<{ role: string }>>(res);
console.log("[listarAutorizacoesUsuario] Rows retornados:", rows);
if (!Array.isArray(rows) || rows.length === 0) {
// sem atribuições, retornamos acesso padrão de paciente
return ["paciente"];
// SEM atribuições = SEM ACESSO ao sistema!
// Usuário precisa ser autorizado por um admin antes de fazer login
console.warn("[listarAutorizacoesUsuario] Usuário sem atribuições - sem acesso!");
return [];
}
return rows
.map((r) =>
r.role === "medico" ? "medico" : ("paciente" as AuthorizationRole),
)
const roles = rows
.map((r) => {
// Mapear role do servidor para role interna
const serverRole = r.role as ServerAuthorizationRole;
return SERVER_ROLE_TO_AUTHORIZATION[serverRole] || null;
})
.filter((role): role is AuthorizationRole => role !== null)
.filter((role, index, self) => self.indexOf(role) === index);
console.log("[listarAutorizacoesUsuario] Roles finais:", roles);
return roles;
}
/**
* Verifica se um usuário tem permissão para fazer login como um determinado tipo
*/
export async function verificarPermissaoLogin(
userId: string,
tipoLogin: "paciente" | "profissional" | "administrador",
): Promise<boolean> {
console.log("[verificarPermissaoLogin] Verificando:", { userId, tipoLogin });
// Admin sempre pode (não usa patient_assignments)
if (tipoLogin === "administrador") {
console.log("[verificarPermissaoLogin] Login admin: PERMITIDO (sem validação de roles)");
return true;
}
// Buscar roles do usuário em ambas as tabelas:
// - patient_assignments (para pacientes)
// - user_roles (para médicos e outros profissionais)
const rolesPatientAssignments = await listarAutorizacoesUsuario(userId);
const rolesUserRoles = await listarUserRolesDoUsuario(userId);
// Combinar roles de ambas as tabelas
const allRoles = [...new Set([...rolesPatientAssignments, ...rolesUserRoles])];
console.log("[verificarPermissaoLogin] Roles em patient_assignments:", rolesPatientAssignments);
console.log("[verificarPermissaoLogin] Roles em user_roles:", rolesUserRoles);
console.log("[verificarPermissaoLogin] Roles combinadas:", allRoles);
// Se não tem nenhuma role em nenhuma tabela, bloqueia acesso
if (allRoles.length === 0) {
console.warn(
"[verificarPermissaoLogin] BLOQUEADO - Usuário sem atribuições",
);
return false;
}
// Mapear tipo de login para role necessária
if (tipoLogin === "profissional") {
const temPermissao = allRoles.includes("medico");
console.log(
"[verificarPermissaoLogin] Login médico:",
temPermissao ? "PERMITIDO" : "BLOQUEADO",
);
return temPermissao;
}
if (tipoLogin === "paciente") {
const temPermissao = allRoles.includes("paciente");
console.log(
"[verificarPermissaoLogin] Login paciente:",
temPermissao ? "PERMITIDO" : "BLOQUEADO",
);
return temPermissao;
}
return false;
}
/**
@ -695,10 +831,11 @@ export async function atualizarAutorizacoesUsuario(
}
// Cria novas atribuições (IMPORTANTE: patient_id é obrigatório!)
// Mapear roles para os valores aceitos pelo servidor
const payload = roles.map((role) => ({
user_id: userId,
patient_id: patientId,
role
role: AUTHORIZATION_ROLE_TO_SERVER[role],
}));
console.log("[atualizarAutorizacoesUsuario] Payload:", payload);
@ -714,9 +851,12 @@ export async function atualizarAutorizacoesUsuario(
const data = await parse<Array<{ role: string }>>(res);
if (!Array.isArray(data)) return [];
return data
.map((r) =>
r.role === "medico" ? "medico" : ("paciente" as AuthorizationRole),
)
.map((r) => {
// Mapear role do servidor para role interna
const serverRole = r.role as ServerAuthorizationRole;
return SERVER_ROLE_TO_AUTHORIZATION[serverRole] || null;
})
.filter((role): role is AuthorizationRole => role !== null)
.filter((v, index, a) => a.indexOf(v) === index);
}
@ -870,11 +1010,11 @@ export async function criarUsuarioMedico(medico: {
}): Promise<CreateUserWithPasswordResponse> {
const senha = gerarSenhaAleatoria();
console.log("🏥 [CRIAR MÉDICO] Iniciando criação no Supabase Auth...");
console.log("📧 Email:", medico.email);
console.log("👤 Nome:", medico.full_name);
console.log("📱 Telefone:", medico.phone_mobile);
console.log("🔑 Senha gerada:", senha);
console.log("[CRIAR MÉDICO] Iniciando criação no Supabase Auth...");
console.log("Email:", medico.email);
console.log("Nome:", medico.full_name);
console.log("Telefone:", medico.phone_mobile);
console.log("Senha gerada:", senha);
// Endpoint do Supabase Auth (mesmo que auth.ts usa)
const signupUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signup`;
@ -889,7 +1029,7 @@ export async function criarUsuarioMedico(medico: {
},
};
console.log("📤 [CRIAR MÉDICO] Enviando para:", signupUrl);
console.log("[CRIAR MÉDICO] Enviando para:", signupUrl);
try {
const response = await fetch(signupUrl, {
@ -903,14 +1043,14 @@ export async function criarUsuarioMedico(medico: {
});
console.log(
"📋 [CRIAR MÉDICO] Status da resposta:",
"[CRIAR MÉDICO] Status da resposta:",
response.status,
response.statusText,
);
if (!response.ok) {
const errorText = await response.text();
console.error("[CRIAR MÉDICO] Erro na resposta:", errorText);
const errorText = await response.text();
console.error("[CRIAR MÉDICO] Erro na resposta:", errorText);
// Tenta parsear o erro para pegar mensagem específica
let errorMessage = `Erro ao criar usuário (${response.status})`;
@ -942,24 +1082,24 @@ export async function criarUsuarioMedico(medico: {
const responseData = await response.json();
console.log(
"[CRIAR MÉDICO] Usuário criado com sucesso no Supabase Auth!",
"[CRIAR MÉDICO] Usuário criado com sucesso no Supabase Auth!",
);
console.log("🆔 User ID:", responseData.user?.id || responseData.id);
console.log("User ID:", responseData.user?.id || responseData.id);
// 🔧 AUTO-CONFIRMAR EMAIL: Fazer login automático logo após criar usuário
// AUTO-CONFIRMAR EMAIL: Fazer login automático logo após criar usuário
// Isso força o Supabase a confirmar o email automaticamente
if (
responseData.user?.email_confirmed_at === null ||
!responseData.user?.email_confirmed_at
) {
console.warn(
"⚠️ [CRIAR MÉDICO] Email NÃO confirmado - tentando auto-confirmar via login...",
"[CRIAR MÉDICO] Email NÃO confirmado - tentando auto-confirmar via login...",
);
try {
const loginUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/token?grant_type=password`;
console.log(
"🔧 [AUTO-CONFIRMAR] Fazendo login automático para confirmar email...",
"[AUTO-CONFIRMAR] Fazendo login automático para confirmar email...",
);
const loginResponse = await fetch(loginUrl, {
@ -977,11 +1117,11 @@ export async function criarUsuarioMedico(medico: {
if (loginResponse.ok) {
const loginData = await loginResponse.json();
console.log(
"[AUTO-CONFIRMAR] Login automático realizado com sucesso!",
"[AUTO-CONFIRMAR] Login automático realizado com sucesso!",
);
console.log(
"📦 [AUTO-CONFIRMAR] Email confirmado:",
loginData.user?.email_confirmed_at ? "SIM" : "NÃO",
"[AUTO-CONFIRMAR] Email confirmado:",
loginData.user?.email_confirmed_at ? "SIM" : "NÃO",
);
// Atualizar responseData com dados do login (que tem email confirmado)
@ -991,39 +1131,37 @@ export async function criarUsuarioMedico(medico: {
} else {
const errorText = await loginResponse.text();
console.error(
"[AUTO-CONFIRMAR] Falha no login automático:",
"[AUTO-CONFIRMAR] Falha no login automático:",
loginResponse.status,
errorText,
);
console.warn(
"⚠️ [AUTO-CONFIRMAR] Usuário pode não conseguir fazer login imediatamente!",
"[AUTO-CONFIRMAR] Usuário pode não conseguir fazer login imediatamente!",
);
}
} catch (confirmError) {
console.error(
"[AUTO-CONFIRMAR] Erro ao tentar fazer login automático:",
"[AUTO-CONFIRMAR] Erro ao tentar fazer login automático:",
confirmError,
);
console.warn(
"⚠️ [AUTO-CONFIRMAR] Continuando sem confirmação automática...",
"[AUTO-CONFIRMAR] Continuando sem confirmação automática...",
);
}
} else {
console.log("[CRIAR MÉDICO] Email confirmado automaticamente!");
console.log("[CRIAR MÉDICO] Email confirmado automaticamente!");
}
// Log bem visível com as credenciais para teste
console.log("🔐🔐🔐 ========================================");
console.log("🔐 CREDENCIAIS DO MÉDICO CRIADO:");
console.log("🔐 Email:", medico.email);
console.log("🔐 Senha:", senha);
console.log("========================================");
console.log("CREDENCIAIS DO MÉDICO CRIADO:");
console.log("Email:", medico.email);
console.log("Senha:", senha);
console.log(
"🔐 Pode fazer login?",
responseData.user?.email_confirmed_at
? "SIM ✅"
: "NÃO ❌ (precisa confirmar email)",
"Pode fazer login?",
responseData.user?.email_confirmed_at ? "SIM" : "NÃO (precisa confirmar email)",
);
console.log("🔐 ========================================");
console.log("========================================");
return {
success: true,
@ -1032,7 +1170,7 @@ export async function criarUsuarioMedico(medico: {
password: senha,
};
} catch (error: any) {
console.error("[CRIAR MÉDICO] Erro ao criar usuário:", error);
console.error("[CRIAR MÉDICO] Erro ao criar usuário:", error);
throw error;
}
}
@ -1045,11 +1183,11 @@ export async function criarUsuarioPaciente(paciente: {
}): Promise<CreateUserWithPasswordResponse> {
const senha = gerarSenhaAleatoria();
console.log("🏥 [CRIAR PACIENTE] Iniciando criação no Supabase Auth...");
console.log("📧 Email:", paciente.email);
console.log("👤 Nome:", paciente.full_name);
console.log("📱 Telefone:", paciente.phone_mobile);
console.log("🔑 Senha gerada:", senha);
console.log("[CRIAR PACIENTE] Iniciando criação no Supabase Auth...");
console.log("Email:", paciente.email);
console.log("Nome:", paciente.full_name);
console.log("Telefone:", paciente.phone_mobile);
console.log("Senha gerada:", senha);
// Endpoint do Supabase Auth (mesmo que auth.ts usa)
const signupUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signup`;
@ -1064,7 +1202,7 @@ export async function criarUsuarioPaciente(paciente: {
},
};
console.log("📤 [CRIAR PACIENTE] Enviando para:", signupUrl);
console.log("[CRIAR PACIENTE] Enviando para:", signupUrl);
try {
const response = await fetch(signupUrl, {
@ -1078,14 +1216,14 @@ export async function criarUsuarioPaciente(paciente: {
});
console.log(
"📋 [CRIAR PACIENTE] Status da resposta:",
"[CRIAR PACIENTE] Status da resposta:",
response.status,
response.statusText,
);
if (!response.ok) {
const errorText = await response.text();
console.error("[CRIAR PACIENTE] Erro na resposta:", errorText);
const errorText = await response.text();
console.error("[CRIAR PACIENTE] Erro na resposta:", errorText);
// Tenta parsear o erro para pegar mensagem específica
let errorMessage = `Erro ao criar usuário (${response.status})`;
@ -1117,17 +1255,17 @@ export async function criarUsuarioPaciente(paciente: {
const responseData = await response.json();
console.log(
"[CRIAR PACIENTE] Usuário criado com sucesso no Supabase Auth!",
"[CRIAR PACIENTE] Usuário criado com sucesso no Supabase Auth!",
);
console.log("🆔 User ID:", responseData.user?.id || responseData.id);
console.log("User ID:", responseData.user?.id || responseData.id);
console.log(
"📦 [CRIAR PACIENTE] Resposta completa do Supabase:",
JSON.stringify(responseData, null, 2),
"[CRIAR PACIENTE] Resposta completa do Supabase:",
JSON.stringify(responseData, undefined, 2),
);
// VERIFICAÇÃO CRÍTICA: O usuário foi realmente criado?
if (!responseData.user && !responseData.id) {
console.error("⚠️⚠️⚠️ AVISO: Supabase retornou sucesso mas SEM user ID!");
console.error("AVISO: Supabase retornou sucesso mas SEM user ID!");
console.error(
"Isso pode significar que o usuário NÃO foi criado de verdade!",
);
@ -1135,20 +1273,20 @@ export async function criarUsuarioPaciente(paciente: {
const userId = responseData.user?.id || responseData.id;
// 🔧 AUTO-CONFIRMAR EMAIL: Fazer login automático logo após criar usuário
// AUTO-CONFIRMAR EMAIL: Fazer login automático logo após criar usuário
// Isso força o Supabase a confirmar o email automaticamente
if (
responseData.user?.email_confirmed_at === null ||
!responseData.user?.email_confirmed_at
) {
console.warn(
"⚠️ [CRIAR PACIENTE] Email NÃO confirmado - tentando auto-confirmar via login...",
"[CRIAR PACIENTE] Email NÃO confirmado - tentando auto-confirmar via login...",
);
try {
const loginUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/token?grant_type=password`;
console.log(
"🔧 [AUTO-CONFIRMAR] Fazendo login automático para confirmar email...",
"[AUTO-CONFIRMAR] Fazendo login automático para confirmar email...",
);
const loginResponse = await fetch(loginUrl, {
@ -1164,29 +1302,29 @@ export async function criarUsuarioPaciente(paciente: {
});
console.log(
"🔍 [AUTO-CONFIRMAR] Status do login automático:",
"[AUTO-CONFIRMAR] Status do login automático:",
loginResponse.status,
);
if (loginResponse.ok) {
const loginData = await loginResponse.json();
console.log(
"[AUTO-CONFIRMAR] Login automático realizado com sucesso!",
"[AUTO-CONFIRMAR] Login automático realizado com sucesso!",
);
console.log(
"📦 [AUTO-CONFIRMAR] Dados completos do login:",
"[AUTO-CONFIRMAR] Dados completos do login:",
JSON.stringify(loginData, undefined, 2),
);
console.log(
"📧 [AUTO-CONFIRMAR] Email confirmado:",
loginData.user?.email_confirmed_at ? "SIM" : "NÃO",
"[AUTO-CONFIRMAR] Email confirmado:",
loginData.user?.email_confirmed_at ? "SIM" : "NÃO",
);
console.log(
"👤 [AUTO-CONFIRMAR] UserType no metadata:",
"[AUTO-CONFIRMAR] UserType no metadata:",
loginData.user?.user_metadata?.userType,
);
console.log(
"🎯 [AUTO-CONFIRMAR] Email verified:",
"[AUTO-CONFIRMAR] Email verified:",
loginData.user?.user_metadata?.email_verified,
);
@ -1197,48 +1335,46 @@ export async function criarUsuarioPaciente(paciente: {
} else {
const errorText = await loginResponse.text();
console.error(
"[AUTO-CONFIRMAR] Falha no login automático:",
"[AUTO-CONFIRMAR] Falha no login automático:",
loginResponse.status,
errorText,
);
console.warn(
"⚠️ [AUTO-CONFIRMAR] Usuário pode não conseguir fazer login imediatamente!",
"[AUTO-CONFIRMAR] Usuário pode não conseguir fazer login imediatamente!",
);
// Tentar parsear o erro para entender melhor
try {
const errorData = JSON.parse(errorText);
console.error("📋 [AUTO-CONFIRMAR] Detalhes do erro:", errorData);
} catch (e) {
console.error("📋 [AUTO-CONFIRMAR] Erro não é JSON:", errorText);
console.error("[AUTO-CONFIRMAR] Detalhes do erro:", errorData);
} catch (error) {
console.error("[AUTO-CONFIRMAR] Erro não é JSON:", errorText);
}
}
} catch (confirmError) {
console.error(
"[AUTO-CONFIRMAR] Erro ao tentar fazer login automático:",
"[AUTO-CONFIRMAR] Erro ao tentar fazer login automático:",
confirmError,
);
console.warn(
"⚠️ [AUTO-CONFIRMAR] Continuando sem confirmação automática...",
"[AUTO-CONFIRMAR] Continuando sem confirmação automática...",
);
}
} else {
console.log("[CRIAR PACIENTE] Email confirmado automaticamente!");
console.log("[CRIAR PACIENTE] Email confirmado automaticamente!");
}
// Log bem visível com as credenciais para teste
console.log("🔐🔐🔐 ========================================");
console.log("🔐 CREDENCIAIS DO PACIENTE CRIADO:");
console.log("🔐 Email:", paciente.email);
console.log("🔐 Senha:", senha);
console.log("🔐 UserType:", "paciente");
console.log("========================================");
console.log("CREDENCIAIS DO PACIENTE CRIADO:");
console.log("Email:", paciente.email);
console.log("Senha:", senha);
console.log("UserType:", "paciente");
console.log(
"🔐 Pode fazer login?",
responseData.user?.email_confirmed_at
? "SIM ✅"
: "NÃO ❌ (precisa confirmar email)",
"Pode fazer login?",
responseData.user?.email_confirmed_at ? "SIM" : "NÃO (precisa confirmar email)",
);
console.log("🔐 ========================================");
console.log("========================================");
return {
success: true,
@ -1247,7 +1383,7 @@ export async function criarUsuarioPaciente(paciente: {
password: senha,
};
} catch (error: any) {
console.error("[CRIAR PACIENTE] Erro ao criar usuário:", error);
console.error("[CRIAR PACIENTE] Erro ao criar usuário:", error);
throw error;
}
}

View File

@ -15,6 +15,7 @@ import {
} from "@/lib/config";
import { debugRequest } from "@/lib/debug-utils";
import { ENV_CONFIG } from "@/lib/env-config";
import { verificarPermissaoLogin } from "@/lib/api";
/**
* Classe de erro customizada para autenticação
@ -179,6 +180,52 @@ export async function loginUser(
);
}
// VALIDAÇÃO CRÍTICA: Verificar se o usuário tem permissão para fazer login com este tipo
const userId = data.user?.id || data.id;
if (userId) {
console.log("[AUTH] Verificando permissões em patient_assignments...");
const temPermissao = await verificarPermissaoLogin(userId, userType);
if (!temPermissao) {
console.error(
"[AUTH] ACESSO NEGADO - Usuário sem permissão para este tipo de login!",
{
userId,
tipoLogin: userType,
},
);
// Mensagem específica por tipo de login
let mensagemErro = "";
if (userType === "paciente") {
mensagemErro =
"Você não tem permissão para acessar a área de pacientes. " +
"Um administrador precisa autorizar seu acesso primeiro. " +
"Entre em contato com a recepção ou administração do sistema.";
} else if (userType === "profissional") {
mensagemErro =
"Você não tem permissão para acessar a área de profissionais. " +
"Um administrador precisa autorizar seu acesso primeiro. " +
"Entre em contato com a administração do sistema.";
} else {
mensagemErro =
"Você não tem permissão para acessar esta área do sistema. " +
"Entre em contato com o administrador.";
}
throw new AuthenticationError(
mensagemErro,
"ACESSO_NEGADO",
{ userId, tipoLogin: userType },
);
}
console.log("[AUTH] Permissões validadas com sucesso!");
}
// Adaptar resposta da sua API para o formato esperado
const adaptedResponse: LoginResponse = {
access_token: data.access_token || data.token,