// Autenticação — login real com Google Identity Services (GIS). // Só entram os e-mails da lista abaixo; qualquer outra conta Google é recusada. // Para funcionar, preencha GOOGLE_CLIENT_ID com o OAuth Client ID (Google Cloud Console // › APIs e Serviços › Credenciais › ID do cliente OAuth › Aplicativo da Web, com a // origem do site em "Origens JavaScript autorizadas"). const GOOGLE_CLIENT_ID = "766556630301-fbppuh81c92q69ssf5mfbt4ks67oe7ah.apps.googleusercontent.com"; const { useState: useStateAu, useEffect: useEffectAu, useRef: useRefAu } = React; const AUTH_SESSION_KEY = "lrfin:session:v2"; const AUTH_USERS = [ { id: "mauro", name: "Mauro Ripani", email: "ripanimauro@gmail.com", color: "#0E92D2" }, { id: "lucas", name: "Lucas Ripani", email: "ripanilucas@gmail.com", color: "#16a34a", admin: true }, ]; function decodeJwt(token) { const part = String(token).split(".")[1]; const json = decodeURIComponent(atob(part.replace(/-/g, "+").replace(/_/g, "/")).split("").map(c => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")); return JSON.parse(json); } const AUTH = { users: AUTH_USERS, clientId: GOOGLE_CLIENT_ID, configured: !!GOOGLE_CLIENT_ID, findByEmail(email) { const e = String(email || "").trim().toLowerCase(); return AUTH_USERS.find(u => u.email === e) || null; }, // Grava a sessão a partir do token assinado pelo Google (exp = validade do próprio token) startSession(user, claims) { const s = { userId: user.id, email: user.email, provider: "google", ts: Date.now(), exp: (claims && claims.exp ? claims.exp * 1000 : Date.now() + 3600e3), sub: claims && claims.sub, picture: claims && claims.picture }; try { localStorage.setItem(AUTH_SESSION_KEY, JSON.stringify(s)); } catch (e) {} return s; }, logout() { try { localStorage.removeItem(AUTH_SESSION_KEY); } catch (e) {} try { if (window.google && google.accounts && google.accounts.id) google.accounts.id.disableAutoSelect(); } catch (e) {} }, getSession() { try { const s = JSON.parse(localStorage.getItem(AUTH_SESSION_KEY)); if (!s || !AUTH_USERS.some(u => u.id === s.userId)) return null; if (s.exp && Date.now() > s.exp) { localStorage.removeItem(AUTH_SESSION_KEY); return null; } return s; } catch (e) { return null; } }, }; function LoginScreen({ onLogin }) { const [erro, setErro] = useStateAu(null); const btnRef = useRefAu(null); useEffectAu(() => { if (!AUTH.configured) return; let cancelled = false; function init() { if (cancelled || !window.google || !google.accounts || !google.accounts.id) return; google.accounts.id.initialize({ client_id: AUTH.clientId, callback: (resp) => { let claims; try { claims = decodeJwt(resp.credential); } catch (e) { setErro("Não foi possível validar a resposta do Google."); return; } const user = AUTH.findByEmail(claims.email); if (!user) { setErro("A conta " + claims.email + " não tem acesso a este planejamento."); try { google.accounts.id.disableAutoSelect(); } catch (e) {} return; } if (claims.email_verified === false) { setErro("E-mail do Google não verificado."); return; } onLogin(AUTH.startSession(user, claims)); }, auto_select: false, cancel_on_tap_outside: true, }); if (btnRef.current) { btnRef.current.innerHTML = ""; google.accounts.id.renderButton(btnRef.current, { theme: "outline", size: "large", type: "standard", text: "continue_with", shape: "pill", locale: "pt-BR", width: 300 }); } } if (window.google && window.google.accounts) init(); else { const s = document.createElement("script"); s.src = "https://accounts.google.com/gsi/client"; s.async = true; s.defer = true; s.onload = init; s.onerror = () => setErro("Não foi possível carregar o login do Google."); document.head.appendChild(s); } return () => { cancelled = true; }; }, []); return (
Acesso restrito. Entre com a conta Google autorizada.
{AUTH.configured ? ( ) : (Crie um OAuth Client ID (tipo Aplicativo da Web) no Google Cloud Console, autorize a origem deste site e cole o ID em GOOGLE_CLIENT_ID, no início de src/auth.jsx.