add-new-authentication-endpoints
This commit is contained in:
parent
b2bdc68319
commit
37a87af28d
@ -301,7 +301,24 @@ export function PatientRegistrationForm({
|
|||||||
const apiMod = await import('@/lib/api');
|
const apiMod = await import('@/lib/api');
|
||||||
const pacienteId = savedPatientProfile?.id || (savedPatientProfile && (savedPatientProfile as any).id);
|
const pacienteId = savedPatientProfile?.id || (savedPatientProfile && (savedPatientProfile as any).id);
|
||||||
const userId = (userResponse.user as any)?.id || (userResponse.user as any)?.user_id || (userResponse.user as any)?.id;
|
const userId = (userResponse.user as any)?.id || (userResponse.user as any)?.user_id || (userResponse.user as any)?.id;
|
||||||
if (pacienteId && userId && typeof apiMod.vincularUserIdPaciente === 'function') {
|
|
||||||
|
// Guard: verify userId is present and looks plausible before attempting to PATCH
|
||||||
|
const isPlausibleUserId = (id: any) => {
|
||||||
|
if (!id) return false;
|
||||||
|
const s = String(id).trim();
|
||||||
|
if (!s) return false;
|
||||||
|
// quick UUID v4-ish check (xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx) or numeric id fallback
|
||||||
|
const uuidV4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||||
|
const numeric = /^\d+$/;
|
||||||
|
return uuidV4.test(s) || numeric.test(s) || s.length >= 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!pacienteId) {
|
||||||
|
console.warn('[PatientForm] pacienteId ausente; pulando vinculação de user_id');
|
||||||
|
} else if (!isPlausibleUserId(userId)) {
|
||||||
|
// Do not attempt to PATCH when userId is missing/invalid to avoid 400s
|
||||||
|
console.warn('[PatientForm] userId inválido ou ausente; não será feita a vinculação. userResponse:', userResponse);
|
||||||
|
} else if (typeof apiMod.vincularUserIdPaciente === 'function') {
|
||||||
console.log('[PatientForm] Vinculando user_id ao paciente:', pacienteId, userId);
|
console.log('[PatientForm] Vinculando user_id ao paciente:', pacienteId, userId);
|
||||||
try {
|
try {
|
||||||
await apiMod.vincularUserIdPaciente(pacienteId, String(userId));
|
await apiMod.vincularUserIdPaciente(pacienteId, String(userId));
|
||||||
|
|||||||
@ -1081,17 +1081,17 @@ export async function excluirPaciente(id: string | number): Promise<void> {
|
|||||||
* Este endpoint usa a service role key e valida se o requisitante é administrador.
|
* Este endpoint usa a service role key e valida se o requisitante é administrador.
|
||||||
*/
|
*/
|
||||||
export async function assignRoleServerSide(userId: string, role: string): Promise<any> {
|
export async function assignRoleServerSide(userId: string, role: string): Promise<any> {
|
||||||
const url = `/api/assign-role`;
|
// Atribuição de roles é uma operação privilegiada que requer a
|
||||||
const token = getAuthToken();
|
// service_role key do Supabase (ou equivalente) e validação de permissões
|
||||||
const res = await fetch(url, {
|
// server-side. Não execute isso do cliente.
|
||||||
method: 'POST',
|
//
|
||||||
headers: {
|
// Antes este helper chamava `/api/assign-role` (um proxy server-side).
|
||||||
'Content-Type': 'application/json',
|
// Agora que o projeto deve usar apenas o endpoint público seguro de
|
||||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
// criação de usuários (OpenAPI `/create-user`), a atribuição deve ocorrer
|
||||||
},
|
// dentro desse endpoint no backend. Portanto este helper foi descontinuado
|
||||||
body: JSON.stringify({ user_id: userId, role }),
|
// no cliente para evitar qualquer tentativa de realizar operação
|
||||||
});
|
// privilegiada no navegador.
|
||||||
return await parse<any>(res);
|
throw new Error('assignRoleServerSide is not available in the client. Use the backend /create-user endpoint which performs role assignment server-side.');
|
||||||
}
|
}
|
||||||
// ===== PACIENTES (Extra: verificação de CPF duplicado) =====
|
// ===== PACIENTES (Extra: verificação de CPF duplicado) =====
|
||||||
export async function verificarCpfDuplicado(cpf: string): Promise<boolean> {
|
export async function verificarCpfDuplicado(cpf: string): Promise<boolean> {
|
||||||
@ -1618,26 +1618,34 @@ export function gerarSenhaAleatoria(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function criarUsuario(input: CreateUserInput): Promise<CreateUserResponse> {
|
export async function criarUsuario(input: CreateUserInput): Promise<CreateUserResponse> {
|
||||||
// When running in the browser, call our Next.js proxy to avoid CORS/preflight
|
// Call the Edge Function directly (no proxy). The backend function is
|
||||||
// issues that some Edge Functions may have. On server-side, call the function
|
// responsible for role assignment and any service-role operations.
|
||||||
// directly.
|
// The OpenAPI for the new endpoint exposes POST /create-user at the
|
||||||
if (typeof window !== 'undefined') {
|
// API root (API_BASE). Call that endpoint directly from the client.
|
||||||
const proxyUrl = '/api/create-user'
|
const url = `${API_BASE}/create-user`;
|
||||||
const res = await fetch(proxyUrl, {
|
|
||||||
|
// Network/fetch errors (including CORS preflight failures) throw before we get a Response.
|
||||||
|
// Catch them and provide a clearer, actionable error message for developers/operators.
|
||||||
|
let res: Response;
|
||||||
|
try {
|
||||||
|
res = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { ...baseHeaders(), 'Content-Type': 'application/json' },
|
headers: { ...baseHeaders(), 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(input),
|
body: JSON.stringify(input),
|
||||||
})
|
});
|
||||||
return await parse<CreateUserResponse>(res as Response)
|
} catch (err: any) {
|
||||||
|
console.error('[criarUsuario] fetch error for', url, err);
|
||||||
|
// Do not attempt client-side signup fallback. Role assignment and user creation
|
||||||
|
// must be performed by the backend endpoint `/create-user` which has the
|
||||||
|
// necessary privileges. Surface a clear error so operators can fix the
|
||||||
|
// backend (CORS / route availability) instead of silently creating an
|
||||||
|
// auth user without roles.
|
||||||
|
throw new Error(
|
||||||
|
'Falha ao contatar o endpoint /create-user. Não será feito fallback via /auth/v1/signup. Verifique se o endpoint /create-user existe, está acessível e se o CORS/OPTIONS está configurado corretamente. Detalhes: ' + (err?.message ?? String(err))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${API_BASE}/functions/v1/create-user`;
|
return await parse<CreateUserResponse>(res as Response);
|
||||||
const res = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { ...baseHeaders(), "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(input),
|
|
||||||
});
|
|
||||||
return await parse<CreateUserResponse>(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== ALTERNATIVA: Criar usuário diretamente via Supabase Auth =====
|
// ===== ALTERNATIVA: Criar usuário diretamente via Supabase Auth =====
|
||||||
@ -1689,7 +1697,15 @@ export async function criarUsuarioDirectAuth(input: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
const userId = responseData.user?.id || responseData.id;
|
// Try several common locations for the returned user id depending on Supabase configuration
|
||||||
|
const userId = responseData?.user?.id || responseData?.id || responseData?.data?.user?.id || responseData?.data?.id;
|
||||||
|
|
||||||
|
// If no user id was returned, treat this as a failure. Some Supabase setups (e.g. magic link / invite)
|
||||||
|
// may not return the user id immediately. In that case we cannot safely link the profile to a user.
|
||||||
|
if (!userId) {
|
||||||
|
console.warn('[DIRECT AUTH] signup response did not include a user id; response:', responseData);
|
||||||
|
throw new Error('Signup did not return a user id (provider may be configured for magic links or pending confirmation). Fallback cannot determine created user id.');
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[DIRECT AUTH] Usuário criado:', userId);
|
console.log('[DIRECT AUTH] Usuário criado:', userId);
|
||||||
|
|
||||||
@ -1723,93 +1739,57 @@ export async function criarUsuarioDirectAuth(input: {
|
|||||||
|
|
||||||
// Criar usuário para MÉDICO no Supabase Auth (sistema de autenticação)
|
// Criar usuário para MÉDICO no Supabase Auth (sistema de autenticação)
|
||||||
export async function criarUsuarioMedico(medico: { email: string; full_name: string; phone_mobile: string; }): Promise<any> {
|
export async function criarUsuarioMedico(medico: { email: string; full_name: string; phone_mobile: string; }): Promise<any> {
|
||||||
// Prefer server-side creation (new OpenAPI create-user) so roles are assigned
|
// Rely on the server-side create-user endpoint (POST /create-user). The
|
||||||
// correctly (and magic link is sent). Fallback to direct Supabase signup if
|
// backend is responsible for role assignment and sending the magic link.
|
||||||
// the server function is unavailable.
|
// Any error should be surfaced to the caller so it can be handled there.
|
||||||
try {
|
return await criarUsuario({ email: medico.email, password: '', full_name: medico.full_name, phone: medico.phone_mobile, role: 'medico' as any });
|
||||||
const res = await criarUsuario({ email: medico.email, password: '', full_name: medico.full_name, phone: medico.phone_mobile, role: 'medico' as any });
|
|
||||||
return res;
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('[CRIAR MÉDICO] Falha no endpoint server-side create-user, tentando fallback direto no Supabase Auth:', err);
|
|
||||||
// Fallback: create directly in Supabase Auth (old behavior)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Fallback to previous direct signup ---
|
|
||||||
const senha = gerarSenhaAleatoria();
|
|
||||||
const signupUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signup`;
|
|
||||||
const payload = {
|
|
||||||
email: medico.email,
|
|
||||||
password: senha,
|
|
||||||
data: {
|
|
||||||
userType: 'profissional',
|
|
||||||
full_name: medico.full_name,
|
|
||||||
phone: medico.phone_mobile,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await fetch(signupUrl, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Accept": "application/json",
|
|
||||||
"apikey": ENV_CONFIG.SUPABASE_ANON_KEY,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(payload),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorText = await response.text();
|
|
||||||
let errorMsg = `Erro ao criar usuário (${response.status})`;
|
|
||||||
try { const errorData = JSON.parse(errorText); errorMsg = errorData.msg || errorData.message || errorData.error_description || errorMsg; } catch {}
|
|
||||||
throw new Error(errorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = await response.json();
|
|
||||||
return { success: true, user: responseData.user || responseData, email: medico.email, password: senha };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Criar usuário para PACIENTE no Supabase Auth (sistema de autenticação)
|
// Criar usuário para PACIENTE no Supabase Auth (sistema de autenticação)
|
||||||
export async function criarUsuarioPaciente(paciente: { email: string; full_name: string; phone_mobile: string; }): Promise<any> {
|
export async function criarUsuarioPaciente(paciente: { email: string; full_name: string; phone_mobile: string; }): Promise<any> {
|
||||||
// Prefer server-side creation (OpenAPI create-user) to assign role 'paciente'.
|
// Rely on the server-side create-user endpoint (POST /create-user).
|
||||||
|
return await criarUsuario({ email: paciente.email, password: '', full_name: paciente.full_name, phone: paciente.phone_mobile, role: 'paciente' as any });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Envia um magic link (OTP) diretamente via Supabase Auth (cliente)
|
||||||
|
* Sem componente server-side adicional. Use quando quiser autenticar
|
||||||
|
* o usuário por email (senha não necessária).
|
||||||
|
*
|
||||||
|
* Observação: isto apenas envia o link de login. A atribuição de roles
|
||||||
|
* continua sendo operação server-side e deve ser feita pelo backend.
|
||||||
|
*/
|
||||||
|
export async function sendMagicLink(email: string, options?: { emailRedirectTo?: string }): Promise<{ success: boolean; message?: string }> {
|
||||||
|
if (!email) throw new Error('Email obrigatório para enviar magic link');
|
||||||
|
const url = `${API_BASE}/auth/v1/otp`;
|
||||||
|
const payload: any = { email };
|
||||||
|
if (options && options.emailRedirectTo) payload.options = { emailRedirectTo: options.emailRedirectTo };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await criarUsuario({ email: paciente.email, password: '', full_name: paciente.full_name, phone: paciente.phone_mobile, role: 'paciente' as any });
|
const res = await fetch(url, {
|
||||||
return res;
|
method: 'POST',
|
||||||
} catch (err) {
|
|
||||||
console.warn('[CRIAR PACIENTE] Falha no endpoint server-side create-user, tentando fallback direto no Supabase Auth:', err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to previous direct signup behavior
|
|
||||||
const senha = gerarSenhaAleatoria();
|
|
||||||
const signupUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signup`;
|
|
||||||
const payload = {
|
|
||||||
email: paciente.email,
|
|
||||||
password: senha,
|
|
||||||
data: {
|
|
||||||
userType: 'paciente',
|
|
||||||
full_name: paciente.full_name,
|
|
||||||
phone: paciente.phone_mobile,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await fetch(signupUrl, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
"Accept": "application/json",
|
Accept: 'application/json',
|
||||||
"apikey": ENV_CONFIG.SUPABASE_ANON_KEY,
|
apikey: ENV_CONFIG.SUPABASE_ANON_KEY,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
const text = await res.text();
|
||||||
const errorText = await response.text();
|
let json: any = null;
|
||||||
let errorMsg = `Erro ao criar usuário (${response.status})`;
|
try { json = text ? JSON.parse(text) : null; } catch { json = null; }
|
||||||
try { const errorData = JSON.parse(errorText); errorMsg = errorData.msg || errorData.message || errorData.error_description || errorMsg; } catch {}
|
|
||||||
throw new Error(errorMsg);
|
if (!res.ok) {
|
||||||
|
const msg = (json && (json.error || json.msg || json.message)) ?? text ?? res.statusText;
|
||||||
|
throw new Error(String(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseData = await response.json();
|
return { success: true, message: (json && (json.message || json.msg)) ?? 'Magic link enviado. Verifique seu email.' };
|
||||||
return { success: true, user: responseData.user || responseData, email: paciente.email, password: senha };
|
} catch (err: any) {
|
||||||
|
console.error('[sendMagicLink] erro ao enviar magic link', err);
|
||||||
|
throw new Error(err?.message ?? 'Falha ao enviar magic link');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== CEP (usado nos formulários) =====
|
// ===== CEP (usado nos formulários) =====
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import type {
|
import type {
|
||||||
LoginRequest,
|
LoginRequest,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
RefreshTokenResponse,
|
|
||||||
AuthError,
|
AuthError,
|
||||||
UserData
|
UserData
|
||||||
} from '@/types/auth';
|
} from '@/types/auth';
|
||||||
@ -89,79 +88,33 @@ export async function loginUser(
|
|||||||
password: string,
|
password: string,
|
||||||
userType: 'profissional' | 'paciente' | 'administrador'
|
userType: 'profissional' | 'paciente' | 'administrador'
|
||||||
): Promise<LoginResponse> {
|
): Promise<LoginResponse> {
|
||||||
// Use server-side AUTH_ENDPOINTS.LOGIN by default. When running in the browser
|
try {
|
||||||
// prefer the local proxy that forwards to the OpenAPI signin: `/api/signin-user`.
|
// Use the canonical Supabase token endpoint for password grant as configured in ENV_CONFIG.
|
||||||
const isBrowser = typeof window !== 'undefined';
|
const url = AUTH_ENDPOINTS.LOGIN;
|
||||||
const url = isBrowser ? '/api/signin-user' : AUTH_ENDPOINTS.LOGIN;
|
|
||||||
|
|
||||||
const payload = {
|
const payload = { email, password };
|
||||||
email,
|
|
||||||
password,
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('[AUTH-API] Iniciando login...', {
|
console.log('[AUTH-API] Iniciando login (using AUTH_ENDPOINTS.LOGIN)...', {
|
||||||
email,
|
email,
|
||||||
userType,
|
userType,
|
||||||
url,
|
url,
|
||||||
payload,
|
|
||||||
timestamp: new Date().toLocaleTimeString()
|
timestamp: new Date().toLocaleTimeString()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Log only non-sensitive info; never log passwords
|
// Do not log passwords. Log only non-sensitive info.
|
||||||
console.log('🔑 [AUTH-API] Credenciais sendo usadas no login (redacted):');
|
debugRequest('POST', url, getLoginHeaders(), { email });
|
||||||
console.log('📧 Email:', email);
|
|
||||||
console.log('👤 UserType:', userType);
|
|
||||||
|
|
||||||
// Delay para visualizar na aba Network
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 50));
|
|
||||||
|
|
||||||
|
// Perform single, explicit request to the configured token endpoint.
|
||||||
|
let response: Response;
|
||||||
try {
|
try {
|
||||||
console.log('[AUTH-API] Enviando requisição de login...');
|
response = await fetch(url, {
|
||||||
|
|
||||||
// Debug: Log request sem credenciais sensíveis
|
|
||||||
debugRequest('POST', url, getLoginHeaders(), payload);
|
|
||||||
|
|
||||||
// Helper to perform a login fetch and return response (no processing here)
|
|
||||||
async function doLoginFetch(targetUrl: string) {
|
|
||||||
try {
|
|
||||||
return await fetch(targetUrl, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getLoginHeaders(),
|
headers: getLoginHeaders(),
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
// bubble up the error to the caller
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let response: Response;
|
|
||||||
try {
|
|
||||||
response = await doLoginFetch(url);
|
|
||||||
} catch (networkError) {
|
} catch (networkError) {
|
||||||
console.warn('[AUTH-API] Network error when calling', url, networkError);
|
console.error('[AUTH-API] Network error when calling', url, networkError);
|
||||||
// Try fallback to server endpoints if available
|
throw new AuthenticationError('Não foi possível contatar o serviço de autenticação', 'AUTH_NETWORK_ERROR', networkError);
|
||||||
const fallback1 = AUTH_ENDPOINTS.LOGIN;
|
|
||||||
const fallback2 = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signin`;
|
|
||||||
let tried = [] as string[];
|
|
||||||
|
|
||||||
try {
|
|
||||||
tried.push(fallback1);
|
|
||||||
response = await doLoginFetch(fallback1);
|
|
||||||
} catch (e1) {
|
|
||||||
console.warn('[AUTH-API] Fallback1 failed', fallback1, e1);
|
|
||||||
try {
|
|
||||||
tried.push(fallback2);
|
|
||||||
response = await doLoginFetch(fallback2);
|
|
||||||
} catch (e2) {
|
|
||||||
console.error('[AUTH-API] All fallbacks failed', { tried, e1, e2 });
|
|
||||||
throw new AuthenticationError(
|
|
||||||
'Não foi possível contatar o serviço de autenticação (todos os caminhos falharam)',
|
|
||||||
'AUTH_NETWORK_ERROR',
|
|
||||||
{ tried }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[AUTH-API] Login response: ${response.status} ${response.statusText}`, {
|
console.log(`[AUTH-API] Login response: ${response.status} ${response.statusText}`, {
|
||||||
@ -170,60 +123,10 @@ export async function loginUser(
|
|||||||
timestamp: new Date().toLocaleTimeString()
|
timestamp: new Date().toLocaleTimeString()
|
||||||
});
|
});
|
||||||
|
|
||||||
// If proxy returned 404, try direct fallbacks (in case the proxy route is missing)
|
// If endpoint is missing, make the error explicit
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
console.warn('[AUTH-API] Proxy returned 404, attempting direct login fallbacks');
|
console.error('[AUTH-API] Final response was 404 (Not Found) for', url);
|
||||||
const fallback1 = AUTH_ENDPOINTS.LOGIN;
|
throw new AuthenticationError('Signin endpoint not found (404) at configured AUTH_ENDPOINTS.LOGIN', 'SIGNIN_NOT_FOUND', { url });
|
||||||
const fallback2 = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signin`;
|
|
||||||
let fallbackResponse: Response | null = null;
|
|
||||||
try {
|
|
||||||
fallbackResponse = await doLoginFetch(fallback1);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('[AUTH-API] fallback1 failed', fallback1, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fallbackResponse || fallbackResponse.status === 404) {
|
|
||||||
try {
|
|
||||||
fallbackResponse = await doLoginFetch(fallback2);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('[AUTH-API] fallback2 failed', fallback2, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fallbackResponse) {
|
|
||||||
response = fallbackResponse;
|
|
||||||
console.log('[AUTH-API] Used fallback response', { url: response.url, status: response.status });
|
|
||||||
} else {
|
|
||||||
console.error('[AUTH-API] No fallback produced a valid response');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Se falhar, mostrar detalhes do erro
|
|
||||||
if (!response.ok) {
|
|
||||||
try {
|
|
||||||
const errorText = await response.text();
|
|
||||||
console.error('[AUTH-API] Erro detalhado:', {
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText,
|
|
||||||
body: errorText,
|
|
||||||
headers: Object.fromEntries(response.headers.entries())
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error('[AUTH-API] Não foi possível ler erro da resposta');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delay adicional para ver status code
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 50));
|
|
||||||
|
|
||||||
// If after trying fallbacks we still have a 404, make the error explicit and actionable
|
|
||||||
if (response.status === 404) {
|
|
||||||
console.error('[AUTH-API] Final response was 404 (Not Found). Likely the local proxy route is missing or Next dev server is not running.', { url: response.url });
|
|
||||||
throw new AuthenticationError(
|
|
||||||
'Signin endpoint not found (404). Ensure Next.js dev server is running and the route `/api/signin-user` exists.',
|
|
||||||
'SIGNIN_NOT_FOUND',
|
|
||||||
{ url: response.url }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await processResponse<any>(response);
|
const data = await processResponse<any>(response);
|
||||||
@ -334,10 +237,10 @@ export async function logoutUser(token: string): Promise<void> {
|
|||||||
/**
|
/**
|
||||||
* Serviço para renovar token JWT
|
* Serviço para renovar token JWT
|
||||||
*/
|
*/
|
||||||
export async function refreshAuthToken(refreshToken: string): Promise<RefreshTokenResponse> {
|
export async function refreshAuthToken(refreshToken: string): Promise<LoginResponse> {
|
||||||
const url = AUTH_ENDPOINTS.REFRESH;
|
const url = AUTH_ENDPOINTS.REFRESH;
|
||||||
|
|
||||||
console.log('[AUTH] Renovando token');
|
console.log('[AUTH] Renovando token via REFRESH endpoint');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
@ -350,10 +253,35 @@ export async function refreshAuthToken(refreshToken: string): Promise<RefreshTok
|
|||||||
body: JSON.stringify({ refresh_token: refreshToken }),
|
body: JSON.stringify({ refresh_token: refreshToken }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await processResponse<RefreshTokenResponse>(response);
|
const data = await processResponse<any>(response);
|
||||||
|
|
||||||
console.log('[AUTH] Token renovado com sucesso');
|
console.log('[AUTH] Dados recebidos no refresh:', data);
|
||||||
return data;
|
|
||||||
|
if (!data || !data.access_token) {
|
||||||
|
console.error('[AUTH] Refresh não retornou access_token:', data);
|
||||||
|
throw new AuthenticationError('Refresh não retornou access_token', 'NO_TOKEN_RECEIVED', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adaptar para o mesmo formato usado no login
|
||||||
|
const adapted: LoginResponse = {
|
||||||
|
access_token: data.access_token || data.token,
|
||||||
|
refresh_token: data.refresh_token || null,
|
||||||
|
token_type: data.token_type || 'Bearer',
|
||||||
|
expires_in: data.expires_in || 3600,
|
||||||
|
user: {
|
||||||
|
id: data.user?.id || data.id || '',
|
||||||
|
email: data.user?.email || data.email || '',
|
||||||
|
name: data.user?.name || data.name || '',
|
||||||
|
userType: (data.user?.userType as any) || 'paciente',
|
||||||
|
profile: data.user?.profile || data.profile || {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('[AUTH] Token renovado com sucesso (adapted)', {
|
||||||
|
tokenSnippet: adapted.access_token?.substring(0, 20) + '...'
|
||||||
|
});
|
||||||
|
|
||||||
|
return adapted;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[AUTH] Erro ao renovar token:', error);
|
console.error('[AUTH] Erro ao renovar token:', error);
|
||||||
|
|
||||||
@ -361,11 +289,7 @@ export async function refreshAuthToken(refreshToken: string): Promise<RefreshTok
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AuthenticationError(
|
throw new AuthenticationError('Não foi possível renovar a sessão', 'REFRESH_ERROR', error);
|
||||||
'Não foi possível renovar a sessão',
|
|
||||||
'REFRESH_ERROR',
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -74,16 +74,25 @@ class HttpClient {
|
|||||||
|
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
|
|
||||||
|
// Data pode ser um LoginResponse completo ou apenas { access_token }
|
||||||
|
const newAccessToken = data.access_token || data.token || null
|
||||||
|
const newRefreshToken = data.refresh_token || null
|
||||||
|
|
||||||
|
if (!newAccessToken) {
|
||||||
|
console.error('[HTTP] Refresh não retornou access_token', data)
|
||||||
|
throw new Error('Refresh did not return access_token')
|
||||||
|
}
|
||||||
|
|
||||||
// Atualizar tokens de forma atômica
|
// Atualizar tokens de forma atômica
|
||||||
localStorage.setItem(AUTH_STORAGE_KEYS.TOKEN, data.access_token)
|
localStorage.setItem(AUTH_STORAGE_KEYS.TOKEN, newAccessToken)
|
||||||
if (data.refresh_token) {
|
if (newRefreshToken) {
|
||||||
localStorage.setItem(AUTH_STORAGE_KEYS.REFRESH_TOKEN, data.refresh_token)
|
localStorage.setItem(AUTH_STORAGE_KEYS.REFRESH_TOKEN, newRefreshToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[HTTP] Token renovado com sucesso!', {
|
console.log('[HTTP] Token renovado com sucesso!', {
|
||||||
timestamp: new Date().toLocaleTimeString()
|
timestamp: new Date().toLocaleTimeString()
|
||||||
})
|
})
|
||||||
return data.access_token
|
return newAccessToken
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user