/** * Serviço de Autenticação (Frontend) * Chama Supabase diretamente */ import axios from "axios"; import { API_CONFIG } from "../api/config"; import type { LoginInput, LoginResponse, AuthUser, RefreshTokenResponse, } from "./types"; class AuthService { /** * Faz login com email e senha */ async login(credentials: LoginInput): Promise { try { console.log("[authService] Tentando login com:", credentials.email); const response = await axios.post( `${API_CONFIG.AUTH_URL}/token?grant_type=password`, { email: credentials.email, password: credentials.password, }, { headers: { "Content-Type": "application/json", apikey: API_CONFIG.SUPABASE_ANON_KEY, }, } ); console.log("[authService] Login bem-sucedido:", response.data); // Salva tokens e user no localStorage if (response.data.access_token) { localStorage.setItem( API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN, response.data.access_token ); localStorage.setItem( API_CONFIG.STORAGE_KEYS.REFRESH_TOKEN, response.data.refresh_token ); localStorage.setItem( API_CONFIG.STORAGE_KEYS.USER, JSON.stringify(response.data.user) ); } return response.data; } catch (error: any) { console.error("[authService] Erro no login:", error); console.error("[authService] Response data:", error.response?.data); console.error("[authService] Response status:", error.response?.status); throw error; } } /** * Registro público (signup) com email e senha * POST /auth/v1/signup * Não requer autenticação - permite auto-registro */ async signup(data: { email: string; password: string; full_name: string; phone?: string; }): Promise { try { const response = await axios.post( `${API_CONFIG.AUTH_URL}/signup`, { email: data.email, password: data.password, options: { data: { full_name: data.full_name, phone: data.phone, }, }, }, { headers: { "Content-Type": "application/json", apikey: API_CONFIG.SUPABASE_ANON_KEY, }, } ); // Salva tokens e user no localStorage if (response.data.access_token) { localStorage.setItem( API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN, response.data.access_token ); localStorage.setItem( API_CONFIG.STORAGE_KEYS.REFRESH_TOKEN, response.data.refresh_token ); localStorage.setItem( API_CONFIG.STORAGE_KEYS.USER, JSON.stringify(response.data.user) ); } return response.data; } catch (error) { console.error("Erro no signup:", error); throw error; } } /** * Envia magic link para o email do usuário * POST /auth/v1/otp */ async sendMagicLink( email: string, redirectUrl?: string ): Promise<{ success: boolean; message: string }> { try { await axios.post( `${API_CONFIG.AUTH_URL}/otp`, { email, options: { emailRedirectTo: redirectUrl || `${API_CONFIG.APP_URL}/auth/callback`, }, }, { headers: { "Content-Type": "application/json", apikey: API_CONFIG.SUPABASE_ANON_KEY, }, } ); return { success: true, message: "Magic link enviado com sucesso", }; } catch (error) { console.error("Erro ao enviar magic link:", error); throw error; } } /** * Solicita reset de senha via email (público) * POST /auth/v1/recover */ async requestPasswordReset( email: string, redirectUrl?: string ): Promise<{ success: boolean; message: string }> { try { await axios.post( `${API_CONFIG.AUTH_URL}/recover`, { email, options: { redirectTo: redirectUrl || `${API_CONFIG.APP_URL}/reset-password`, }, }, { headers: { "Content-Type": "application/json", apikey: API_CONFIG.SUPABASE_ANON_KEY, }, } ); return { success: true, message: "Email de recuperação de senha enviado com sucesso. Verifique sua caixa de entrada.", }; } catch (error) { console.error("Erro ao solicitar reset de senha:", error); throw error; } } /** * Atualiza a senha do usuário usando o token de recuperação * PUT /auth/v1/user */ async updatePassword( accessToken: string, newPassword: string ): Promise<{ success: boolean; message: string }> { try { await axios.put( `${API_CONFIG.AUTH_URL}/user`, { password: newPassword, }, { headers: { "Content-Type": "application/json", apikey: API_CONFIG.SUPABASE_ANON_KEY, Authorization: `Bearer ${accessToken}`, }, } ); return { success: true, message: "Senha atualizada com sucesso", }; } catch (error) { console.error("Erro ao atualizar senha:", error); throw error; } } /** * Faz logout (invalida sessão no servidor e limpa localStorage) */ async logout(): Promise { try { const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN); if (token) { // Chama API para invalidar sessão no servidor await axios.post( `${API_CONFIG.AUTH_URL}/logout`, {}, { headers: { "Content-Type": "application/json", apikey: API_CONFIG.SUPABASE_ANON_KEY, Authorization: `Bearer ${token}`, }, } ); } } catch (error) { console.error("Erro ao invalidar sessão no servidor:", error); // Continua mesmo com erro, para garantir limpeza local } finally { // Sempre limpa o localStorage localStorage.removeItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN); localStorage.removeItem(API_CONFIG.STORAGE_KEYS.REFRESH_TOKEN); localStorage.removeItem(API_CONFIG.STORAGE_KEYS.USER); } } /** * Verifica se usuário está autenticado */ isAuthenticated(): boolean { return !!localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN); } /** * Retorna o usuário atual do localStorage */ getCurrentUser(): AuthUser | null { const userStr = localStorage.getItem(API_CONFIG.STORAGE_KEYS.USER); if (!userStr) return null; try { return JSON.parse(userStr); } catch { return null; } } /** * Retorna o access token */ getAccessToken(): string | null { return localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN); } /** * Renova o access token usando o refresh token */ async refreshToken(): Promise { try { const refreshToken = localStorage.getItem( API_CONFIG.STORAGE_KEYS.REFRESH_TOKEN ); if (!refreshToken) { throw new Error("Refresh token não encontrado"); } const response = await axios.post( `${API_CONFIG.AUTH_URL}/token?grant_type=refresh_token`, { refresh_token: refreshToken, }, { headers: { "Content-Type": "application/json", apikey: API_CONFIG.SUPABASE_ANON_KEY, }, } ); // Atualiza tokens e user no localStorage if (response.data.access_token) { localStorage.setItem( API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN, response.data.access_token ); localStorage.setItem( API_CONFIG.STORAGE_KEYS.REFRESH_TOKEN, response.data.refresh_token ); localStorage.setItem( API_CONFIG.STORAGE_KEYS.USER, JSON.stringify(response.data.user) ); } return response.data; } catch (error) { console.error("Erro ao renovar token:", error); // Se falhar, limpa tudo e força novo login this.logout(); throw error; } } } export const authService = new AuthService();