# Implementação: Captura de Email do Google OAuth ## Problema O card do Google Calendar estava exibindo o email de login do Supabase, não o email da conta Google usada no OAuth. ## Solução Implementada ### 1. **Adicionar Escopos OpenID Connect** Arquivo: `app/api/google-calendar/auth/route.ts` Adicionados escopos `openid` e `email` para receber `id_token` com informações do usuário: ```typescript const scopes = [ "openid", // ✅ NOVO "email", // ✅ NOVO "https://www.googleapis.com/auth/gmail.modify", "https://www.googleapis.com/auth/calendar.readonly", "https://www.googleapis.com/auth/calendar.events", ].join(" "); ``` ### 2. **Extrair Email do id_token** Arquivo: `app/api/google-calendar/callback/route.ts` O Google retorna um `id_token` (JWT) que contém o email do usuário: ```typescript // Decodificar id_token (JWT base64) let googleEmail: string | null = null; if (tokens.id_token) { try { const payloadBase64 = tokens.id_token.split('.')[1]; const payloadJson = Buffer.from(payloadBase64, 'base64').toString('utf-8'); const payload = JSON.parse(payloadJson); googleEmail = payload.email || null; console.log("[callback] Email extraído do id_token:", googleEmail); } catch (error) { console.error("[callback] Erro ao decodificar id_token:", error); } } // Fallback: Se falhar, buscar via API userinfo if (!googleEmail && tokens.access_token) { const userinfoResponse = await fetch( 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json', { headers: { Authorization: `Bearer ${tokens.access_token}` }, } ); if (userinfoResponse.ok) { const userinfo = await userinfoResponse.json(); googleEmail = userinfo.email || null; } } ``` ### 3. **Salvar Email no Banco** Arquivo: `app/api/google-calendar/manage-credential/route.ts` Adicionar `googleEmail` ao upsert no Supabase: ```typescript const { error: upsertError } = await supabase .schema("portal") .from("integrations") .upsert({ user_id: user.id, provider: "google_calendar", status: "connected", n8n_credential_id: credentialId, n8n_credential_name: "refugio", connected_email: googleEmail, // ✅ NOVO connected_at: new Date().toISOString(), updated_at: new Date().toISOString(), }, { onConflict: "user_id,provider", }); ``` ### 4. **Exibir Email Correto no Dashboard** Arquivo: `app/dashboard/page.tsx` Buscar `connected_email` do banco ao invés de usar `user.email`: ```typescript const { data, error } = await supabase .schema("portal") .from("integrations") .select("status, connected_at, n8n_credential_id, n8n_credential_name, connected_email") // ✅ NOVO .eq("user_id", user.id) .eq("provider", "google_calendar") .maybeSingle(); if (data && data.status === "connected") { setCalendarConnected(true); setCalendarCredentialId(data.n8n_credential_id || null); setCalendarCredentialName(data.n8n_credential_name || null); setCalendarEmail(data.connected_email || null); // ✅ USA EMAIL DO GOOGLE, NÃO DO SUPABASE } ``` ## Fluxo Completo ``` 1. Usuário clica em "Conectar" no Dashboard ↓ 2. GET /api/google-calendar/auth - Gera URL OAuth com escopos: openid, email, calendar, gmail ↓ 3. Usuário autoriza no Google (escolhe conta) ↓ 4. Google redireciona para /api/google-calendar/callback - Recebe: code, state, id_token ↓ 5. Callback troca code por tokens: - access_token - refresh_token - id_token (JWT com email do usuário) ↓ 6. Extrai email do id_token (decodifica JWT) - Fallback: Busca via userinfo API ↓ 7. POST /api/google-calendar/manage-credential - Cria credencial no n8n - Salva no Supabase: n8n_credential_id, connected_email ↓ 8. Dashboard recarrega e exibe: 📧 Conectado como: usuario@gmail.com ← Email do Google OAuth 🔑 Credencial n8n: refugio ID: abc123xyz ``` ## Estrutura JWT do id_token O `id_token` é um JWT com 3 partes separadas por `.`: ``` eyJhbGciOi... . eyJlbWFpbCI6InVzZXJAZ21haWwuY29tIi... . signature │ │ │ │ │ │ └─ Payload (contém email, sub, etc) │ │ └─ Separador │ └─ Header Assinatura ────┘ ``` Decodificamos a **parte 2 (payload)** que contém: ```json { "email": "usuario@gmail.com", "email_verified": true, "sub": "1234567890", "iss": "https://accounts.google.com", ... } ``` ## Compatibilidade ✅ **Não requer migration SQL** - Coluna `connected_email` já existe na tabela ✅ **Não quebra workflows n8n** - Nome da credencial continua "refugio" ✅ **Backward compatible** - Se falhar extração, usa fallback via API ## Teste 1. Executar SQL para adicionar `n8n_credential_id` (se ainda não foi feito) 2. Deletar credenciais órfãs 3. Fazer logout e login no dashboard 4. Clicar em "Conectar" no card Google Calendar 5. Autorizar com conta Google diferente do email de login 6. Verificar que o card exibe o email correto da conta Google ## Logs Esperados ``` [auth] OAuth URL gerada para user: 7cd11392-e239-4cbb-bb38-d51272004b26 [callback] User: 7cd11392-e239-4cbb-bb38-d51272004b26 - State validated [callback] Tokens obtidos do Google [callback] Email extraído do id_token: usuario@gmail.com [manage-credential] POST - User: 7cd11392-e239-4cbb-bb38-d51272004b26 [manage-credential] Google email received: usuario@gmail.com [n8n-api] Creating new credential "refugio" [n8n-api] Credential created successfully: abc123xyz [manage-credential] Integration status saved to Supabase ``` ## Referências - [Google OAuth 2.0 Documentation](https://developers.google.com/identity/protocols/oauth2/web-server) - [OpenID Connect Specification](https://openid.net/specs/openid-connect-core-1_0.html) - [JWT.io - Debugger para tokens](https://jwt.io/)