Dashboard-Automatizase/docs/n8n-webhook-google-calendar.md
Luis Erlacher 0152a2fda0 feat: add n8n API testing script for Google OAuth2 schema and existing credentials
- Implemented a bash script to test n8n API and retrieve credential schemas.
- Added types for API responses, Google Calendar, and WhatsApp instances.
- Configured Vitest for testing with React and added setup for testing-library.
2025-10-10 14:29:02 -03:00

10 KiB

Webhook n8n para Google Calendar OAuth

Visão Geral

O portal enviará uma requisição webhook ao n8n quando o usuário clicar em "Conectar Google Calendar". O n8n será responsável por:

  1. Receber o webhook com os dados do usuário
  2. Iniciar o fluxo OAuth do Google Calendar
  3. Armazenar as credenciais OAuth no n8n
  4. Salvar o status da integração no Supabase
  5. Notificar o portal sobre o sucesso/erro

Fluxo de Integração

Portal Dashboard
    │
    ├─> Usuário clica "Conectar Google Calendar"
    │
    ├─> Portal envia POST para webhook n8n
    │   Payload: { user_id, user_email, redirect_uri }
    │
    └─> n8n recebe webhook
        │
        ├─> n8n inicia OAuth Google Calendar
        │   - redirect_uri aponta para n8n
        │   - Usuário autoriza no Google
        │
        ├─> n8n recebe callback OAuth do Google
        │   - Salva credenciais no n8n credentials store
        │
        ├─> n8n salva status no Supabase
        │   UPDATE portal.integrations
        │   SET status = 'connected', connected_email = :email
        │   WHERE user_id = :user_id AND provider = 'google_calendar'
        │
        └─> n8n redireciona usuário de volta ao portal
            - URL: {portal_redirect_uri}?oauth_success=true
            - Em caso de erro: {portal_redirect_uri}?oauth_error={message}

Endpoint do Webhook n8n

URL

POST https://n8n.automatizase.com.br/webhook/google-calendar-sync

Request Headers

Content-Type: application/json

Request Body

{
  "user_id": "uuid-do-usuario-supabase",
  "user_email": "usuario@exemplo.com",
  "redirect_uri": "https://portal.automatizase.com.br/dashboard"
}

Response (Imediata)

{
  "status": "processing",
  "message": "OAuth flow iniciado. Você será redirecionado em instantes.",
  "oauth_url": "https://accounts.google.com/o/oauth2/v2/auth?..."
}

O n8n deve redirecionar o usuário para oauth_url imediatamente após receber a requisição.


Configuração OAuth no Google Cloud Console

Criar Credenciais OAuth 2.0

  1. Acesse Google Cloud Console

  2. Crie um novo projeto ou selecione um existente

  3. Navegue para APIs & Services > Credentials

  4. Clique em Create Credentials > OAuth 2.0 Client ID

  5. Configure:

    • Application type: Web application
    • Name: AutomatizaSE Portal - Google Calendar
    • Authorized redirect URIs:
      https://n8n.automatizase.com.br/webhook/google-calendar-oauth-callback
      
  6. Anote o Client ID e Client Secret

Escopos Necessários

https://www.googleapis.com/auth/calendar.readonly
https://www.googleapis.com/auth/calendar.events

Implementação no n8n

Workflow n8n

1. Webhook Node (Trigger)

  • Webhook Path: /google-calendar-sync
  • Method: POST
  • Authentication: None (ou Basic Auth se quiser proteger)
  • Response Mode: Immediately
  • Response Data: JSON

2. Set Node (Preparar OAuth URL)

{
  "user_id": "{{$json.body.user_id}}",
  "user_email": "{{$json.body.user_email}}",
  "redirect_uri": "{{$json.body.redirect_uri}}",
  "oauth_url": "https://accounts.google.com/o/oauth2/v2/auth?client_id={{$env.GOOGLE_CLIENT_ID}}&redirect_uri=https://n8n.automatizase.com.br/webhook/google-calendar-oauth-callback&response_type=code&scope=https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/calendar.events&access_type=offline&state={{$json.body.user_id}}"
}

3. Respond to Webhook Node

{
  "status": "processing",
  "oauth_url": "{{$json.oauth_url}}"
}

4. HTTP Redirect (Redirecionar para Google OAuth)

  • Use um nó HTTP Request ou Respond to Webhook com redirect para enviar o usuário para o Google

5. Webhook Node (OAuth Callback)

  • Webhook Path: /google-calendar-oauth-callback
  • Method: GET
  • Authentication: None
  • Recebe:
    • code (authorization code do Google)
    • state (user_id)

6. HTTP Request (Trocar Code por Token)

POST https://oauth2.googleapis.com/token
Body:
{
  "code": "{{$json.query.code}}",
  "client_id": "{{$env.GOOGLE_CLIENT_ID}}",
  "client_secret": "{{$env.GOOGLE_CLIENT_SECRET}}",
  "redirect_uri": "https://n8n.automatizase.com.br/webhook/google-calendar-oauth-callback",
  "grant_type": "authorization_code"
}

Response:

{
  "access_token": "ya29.xxx",
  "refresh_token": "1//xxx",
  "expires_in": 3599,
  "token_type": "Bearer"
}

7. HTTP Request (Obter Email do Usuário Google)

GET https://www.googleapis.com/oauth2/v1/userinfo?access_token={{$json.access_token}}

Response:

{
  "email": "usuario@gmail.com",
  "verified_email": true
}

8. Save Credentials to n8n Store

  • Use o nó n8n Credentials para salvar o access_token e refresh_token
  • Associar ao user_id vindo do state

9. Supabase Node (Update Integration Status)

INSERT INTO portal.integrations (user_id, provider, status, connected_email, connected_at, updated_at)
VALUES (:user_id, 'google_calendar', 'connected', :google_email, NOW(), NOW())
ON CONFLICT (user_id, provider)
DO UPDATE SET
  status = 'connected',
  connected_email = :google_email,
  connected_at = NOW(),
  updated_at = NOW();

Parâmetros:

  • :user_id = {{$json.query.state}} (user_id vindo do state)
  • :google_email = {{$json.email}} (email do Google)

10. HTTP Redirect (Voltar ao Portal)

Redirect to: {{$node["Webhook"].json.body.redirect_uri}}?oauth_success=true

Em caso de erro:

Redirect to: {{$node["Webhook"].json.body.redirect_uri}}?oauth_error={{$json.error_message}}

Variáveis de Ambiente no n8n

GOOGLE_CLIENT_ID=xxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxx
SUPABASE_URL=https://xxxxx.supabase.co
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxxxx

Tratamento de Erros

Cenários de Erro

Erro Causa Ação do n8n
User cancela OAuth Usuário clica "Cancelar" no Google Redirecionar com oauth_error=user_cancelled
Token inválido Google rejeita o code Redirecionar com oauth_error=invalid_token
Supabase falha Erro ao salvar status Redirecionar com oauth_error=database_error
Credenciais inválidas Client ID/Secret errados Redirecionar com oauth_error=config_error

Response de Erro

{
  "status": "error",
  "error_message": "Descrição do erro"
}

Segurança

Validações Necessárias

  1. Validar user_id: Verificar se é um UUID válido
  2. Validar redirect_uri: Whitelist de domínios permitidos
    const allowedDomains = [
      'https://portal.automatizase.com.br',
      'http://localhost:3000' // apenas dev
    ]
    
  3. State Parameter: Usar user_id como state para evitar CSRF
  4. HTTPS Only: Todas as URLs devem usar HTTPS em produção

Testes

Teste Manual

  1. Testar Webhook:

    curl -X POST https://n8n.automatizase.com.br/webhook/google-calendar-sync \
      -H "Content-Type: application/json" \
      -d '{
        "user_id": "123e4567-e89b-12d3-a456-426614174000",
        "user_email": "teste@exemplo.com",
        "redirect_uri": "http://localhost:3000/dashboard"
      }'
    
  2. Verificar Redirect: Deve retornar uma URL OAuth do Google

  3. Completar OAuth: Clicar no link e autorizar

  4. Verificar Supabase:

    SELECT * FROM portal.integrations WHERE user_id = '123e4567-e89b-12d3-a456-426614174000';
    
  5. Verificar Redirect: Deve voltar ao portal com ?oauth_success=true


Exemplo de Implementação Simplificada no Portal

Component: GoogleCalendarCard.tsx

const handleConnectGoogleCalendar = async () => {
  try {
    setLoading(true)

    const { data: { user } } = await supabase.auth.getUser()
    if (!user) throw new Error('User not authenticated')

    // Enviar webhook ao n8n
    const response = await fetch(process.env.NEXT_PUBLIC_N8N_WEBHOOK_GOOGLE_CALENDAR!, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        user_id: user.id,
        user_email: user.email,
        redirect_uri: `${window.location.origin}/dashboard`
      })
    })

    const data = await response.json()

    if (data.oauth_url) {
      // Redirecionar para OAuth do Google
      window.location.href = data.oauth_url
    } else {
      throw new Error('Failed to initiate OAuth flow')
    }
  } catch (error) {
    console.error('Error connecting Google Calendar:', error)
    setToastMessage('Erro ao conectar Google Calendar')
    setToastType('error')
    setToastVisible(true)
  } finally {
    setLoading(false)
  }
}

Environment Variable

NEXT_PUBLIC_N8N_WEBHOOK_GOOGLE_CALENDAR=https://n8n.automatizase.com.br/webhook/google-calendar-sync

Checklist de Implementação

n8n

  • Criar workflow no n8n
  • Configurar webhook /google-calendar-sync
  • Configurar webhook /google-calendar-oauth-callback
  • Obter Google OAuth Client ID e Secret
  • Configurar variáveis de ambiente no n8n
  • Adicionar redirect URI no Google Cloud Console
  • Implementar lógica de troca de code por token
  • Implementar salvamento no Supabase
  • Implementar redirect de volta ao portal
  • Testar fluxo completo

Portal

  • Atualizar GoogleCalendarCard para usar webhook
  • Adicionar variável NEXT_PUBLIC_N8N_WEBHOOK_GOOGLE_CALENDAR
  • Remover lógica OAuth direta do portal
  • Manter lógica de callback para receber ?oauth_success=true
  • Testar integração end-to-end

Benefícios desta Abordagem

  1. Segurança: Credenciais OAuth ficam apenas no n8n
  2. Centralização: n8n gerencia todas as credenciais
  3. Manutenção: Mais fácil atualizar lógica OAuth
  4. Reutilização: Outros workflows n8n podem usar as mesmas credenciais
  5. Isolamento: Portal não precisa lidar com complexidade do OAuth

Referências