8.2 KiB
C149 — Google Calendar: botão Conectar Google (OAuth) substitui a configuração manual de service account
Status: rascunho Atualizado em: 2026-08-27 Issue: #934 Priority: P2 Model: composer-2.5 Depends: #932 (C147) Impeccable: B — encaixe na agenda (configuração do sync) Rascunho UI: docs/plans/c149-conectar-google-oauth-ui-draft.html Appetite: ~2 dias eng; um outcome verificável — o candidato/coordenador conecta a conta Google do calendário da campanha clicando um botão (consent do Google), sem criar service account nem colar JSON Responsável: —
Intenção
Ativar o espelho Google Calendar (C114) hoje exige cerimônia de engenharia: criar service account no GCP, baixar JSON, base64, colar em env, compartilhar o calendário com o e-mail da conta de serviço. O dono do produto: "This manual configuration of the service account plus calendar isn't ideal — isn't there a way to expose a button that allows the candidate to sync a calendar to it?" O registro C114 recusou OAuth por pessoa (N staff escrevendo no calendário pessoal de cada um). O pedido atual é diferente: uma concessão OAuth em que candidato/coordenador autoriza o Teqo a gerenciar o calendário da campanha — variante não coberta pelo registro, vinda como nova evidência do dono. Configuração vira um clique.
Persona e fluxo
- Persona / contexto: candidato ou coordenador geral, no computador (mesa), configurando o espelho da agenda uma única vez — e voltando só quando algo quebra.
- Job principal: "conecto a conta Google do calendário da campanha ao Teqo sem ajuda de engenharia".
- Fluxo desejado: na configuração do sync da agenda → clica "Conectar com o Google" → tela de consent do Google (escolhe a conta da campanha) → volta ao Teqo com estado "conectado" → o sync passa a usar essa conexão; se o token quebrar um dia, o estado mostra "erro" com caminho de reconexão (clicar de novo).
- Anti-goals de produto: não é OAuth por pessoa (C114 continua valendo — UMA concessão, calendário da campanha); não é migração obrigatória (service account segue funcionando como fallback); não mexe na seleção de qual calendário (C150) nem no motor de reconciliação; leader não vê nem conecta (lockdown).
Esboço de fluxo (B)
[/campanha/agenda — configuração do sync] → "Conectar com o Google" → consent Google (conta da campanha)
→ callback de volta ao Teqo → estado "conectado" → sync usa a conexão (service account vira fallback)
Rascunho UI (gate)
- Rascunho UI (gate):
docs/plans/c149-conectar-google-oauth-ui-draft.html— cenas: não configurado (botão), conectado, erro de token com reconexão, mobile.
Objetivo e aceite
- O candidato/coordenador conecta a conta Google do calendário da campanha clicando um botão (consent do Google) — sem criar service account, sem colar JSON, sem tocar em GCP.
- Estado da conexão visível e honesto: conectado | erro | não configurado; erro de token mostra caminho de reconexão em um clique.
- Falha de token/Google não corrompe dados: Teqo continua SoT; o espelho degrada para "erro", nada é apagado nem duplicado.
- Funciona atrás do túnel Cloudflare: o callback OAuth usa a URL pública correta (nada de localhost vazando no redirect).
- Guardrails: o token nunca aparece em log; armazenamento fail-closed (sem conexão → sem sync, sem crash); desconectar revoga o acesso no Teqo e orienta revogar no Google; leader lockdown intocado.
Dados (intenção)
- Vou apresentar dados? Não — a conexão é infraestrutura do espelho (saída); nenhuma leitura nova de dados no Teqo. O
calendarList(escopo readonly) alimenta apenas a escolha de calendário do C150. - Decisões desbloqueadas: candidato/coordenador decide (uma vez) qual conta Google autoriza; diante de "erro", o mesmo ator decide reconectar ou desconectar.
- Forma: adiada ao plano de implementação — aqui só as restrições: estado ternário, nunca exibir o token, sem inventar estados intermediários.
Dados da decisão (literais)
- Escopos OAuth mínimos:
https://www.googleapis.com/auth/calendar.events+https://www.googleapis.com/auth/calendar.calendarlist.readonly(o segundo basta para listar calendários no C150 — sem escopo além do necessário). - Estados da conexão (UI + registro):
conectado | erro | não configurado— exatamente esses três. - Env de credencial que vira fallback (não renomear nem remover nesta entrega):
GOOGLE_CALENDAR_SERVICE_ACCOUNT_KEY. - Restrição dura de produto: o token DEVE sobreviver sem re-consentimento semanal (o caminho técnico — app unverified vs verificação Google — fica em aberto, ver Questões).
Direção no codebase (hipótese)
- Áreas prováveis: configuração do sync na agenda
/campanha/agenda(botão/estado), rotas de callback OAuth emsrc/app/(campaign)/campanha/…, utilitários Google perto degoogleCalendarSync.ts/googleCalendarClient.ts, armazenamento da conexão na collectiongoogleCalendarSync(ondecalendarIdjá vive). - Precedente a olhar: plano C114 (decisão "não OAuth por pessoa"); par
readGoogleServiceAccountCredentials/googleCalendarClient(REST hand-rolled comjose, sem lib Google hoje); WebAuthn de campaignUser (precedente de cerimônia de credencial com rota fora de(app)). - Risco de acoplamento: o token é credencial da conta Google da campanha — nunca logar, fail-closed, desconectar revoga; leader lockdown; C147 merged antes (o sync funcionando/diagnosticado) para não brigar no mesmo client.
Dependências
- Dura: C147 — o sync precisa estar funcionando/diagnosticado antes de trocar o modelo de credencial (também evita conflito de arquivos no client).
Fora de escopo
- Escolha/listagem de qual calendário conectar → C150 (aqui só entrega a conexão e o estado).
- Migração/remoção obrigatória da service account (fica como fallback; aposentar é entrega futura).
- OAuth por pessoa (anti-goal C114, reafirmado) e leitura de agenda pessoal para conflito de horário (item separado, se um dia existir).
- Motor de reconciliação do sync (janela, locks, hooks C114/C122) — intocado.
Rabbit holes de produto
- "Verificação completa do app no Google." Se alguém "só completar": auditoria, casework, semanas de fila — para uso interno. Corte neste item: caminho unverified (uso interno, <100 usuários) com aviso no consent; verificação só se o produto um dia precisar.
- "Desconectar + limpar tudo + migrar quem usa service account." Corte: desconectar = revogar no Teqo + orientação de revogar no Google; ninguém é obrigado a migrar nesta entrega.
- "Seletor de calendário dentro do botão." Se alguém "só completar": puxa o C150 para dentro. Corte: a conexão autoriza; escolher o calendário é o item seguinte.
Questões em aberto (produto)
- Manter a service account como fallback depois que o OAuth existir? Opções: A) sim, como caminho técnico até o OAuth estar comprovado em produção | B) aposentar na mesma entrega. Recomendação: A — aposentar é entrega separada. (assumido — validar com produto)
- Quem pode conectar/desconectar? Opções: A) candidato + coordenador | B) só candidato | C) também advisor. Recomendação: A — é a credencial da campanha inteira; advisor não autoriza credencial global, leader lockdown. (assumido — validar)
- App unverified ou verificação Google? Opções: A) unverified (aviso no consent, token não expira em ~7 dias) | B) verificação completa (sem aviso, custo de auditoria). Recomendação: A — uso interno, <100 usuários; a restrição dura é o token sobreviver sem re-consentimento semanal. (trade-off do aviso é a decisão de produto)
Referências
- Rascunho UI (gate):
docs/plans/c149-conectar-google-oauth-ui-draft.html - Plan C114 (
c114-google-calendar-push-agenda-oficial.md— decisão "não OAuth por pessoa"); C147 (dependência dura); C150 (seleção de calendário, fora daqui) src/utilities/googleCalendarSync.ts,src/utilities/googleCalendarClient.ts, collectiongoogleCalendarSync(calendarId)AGENTS-campaign.md— leader lockdown / papéis de/campanha