- 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.
9.9 KiB
⚠️ DOCUMENTO OBSOLETO
Este documento descreve uma abordagem antiga que não é mais recomendada.
Para o fluxo atual de OAuth Google Calendar com interceptação de callback, consulte:
- Google Calendar Callback Interception ✅ RECOMENDADO
Configuração do Webhook n8n para Atualizar Credencial Google
⚡ TL;DR - Escolha seu caminho
🟢 Caminho Fácil (RECOMENDADO)
Configure OAuth manualmente no n8n UI (1 clique). Pular para solução →
Vantagens: Simples, rápido, sem código, funciona 100%
🔴 Caminho Complexo (AVANÇADO)
Criar webhook que atualiza credencial via SQL no PostgreSQL. Continue lendo ↓
Desvantagens: Requer pgcrypto, acesso superuser, configuração SQL complexa
Problema
A API REST do n8n tem um bug no schema de validação de credenciais OAuth2 do Google (googleOAuth2Api). O schema valida grantType mas não permite esse campo devido a additionalProperties: false.
Solução (Caminho Complexo)
Criar um workflow no n8n que atualiza a credencial diretamente no banco de dados PostgreSQL.
Workflow no n8n
1. Webhook (Trigger)
- URL:
/webhook/update-google-credential - Método:
POST - Autenticação: Nenhuma (ou adicionar secret token)
Body esperado:
{
"userId": "uuid-do-usuario",
"credentialId": "Y9Izvj7XzExRNw2W",
"credentialData": {
"clientId": "...",
"clientSecret": "...",
"scope": "https://www.googleapis.com/auth/calendar",
"oauthTokenData": {
"access_token": "ya29...",
"refresh_token": "1//0g...",
"scope": "https://www.googleapis.com/auth/calendar",
"token_type": "Bearer",
"expiry_date": 1735689600000
}
}
}
2. Set Node (Preparar Dados)
Criar variável credentialDataJson com o JSON stringify dos dados:
// Code:
const credentialData = $json.credentialData;
return {
json: {
credentialDataJson: JSON.stringify(credentialData),
credentialId: $json.credentialId,
userId: $json.userId
}
};
3. Postgres Node (Atualizar Credencial)
Operação: Execute Query
IMPORTANTE: Verificar o schema das tabelas do n8n primeiro:
-- Descobrir schema correto
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_name = 'credentials';
Query SQL (n8n usa TypeORM com camelCase):
UPDATE public.credentials_entity
SET
data = pgp_sym_encrypt(
$1::text,
(SELECT value FROM public.settings WHERE key = 'encryptionKey')
),
"updatedAt" = NOW()
WHERE id = $2
RETURNING id, name, type, "updatedAt";
IMPORTANTE:
- Tabela:
credentials_entity(nãocredentials) - Coluna:
updatedAtcom aspas duplas (camelCase do TypeORM)
Configuração de Parâmetros no n8n:
Opção 1 - Parâmetros Posicionais ($1, $2):
Campo Options > Query Parameters: data,credentialId
Campo Options > Query Parameters Values:
={{ $json.credentialDataJson }},={{ $json.credentialId }}
Opção 2 - Named Parameters (:nome):
Campo: Query
UPDATE public.credentials_entity
SET data = pgp_sym_encrypt(:data::text, (SELECT value FROM public.settings WHERE key = 'encryptionKey')), "updatedAt" = NOW()
WHERE id = :credentialId
RETURNING id, name, type, "updatedAt";
Campo: Options > Query Parameters = data,credentialId
Campo: Options > Query Parameters Values = ={{ $json.credentialDataJson }},={{ $json.credentialId }}
4. Response Node (Retornar Sucesso)
{
"success": true,
"credentialId": "{{ $json.id }}",
"updatedAt": "{{ $json.updated_at }}",
"message": "Credencial atualizada com sucesso"
}
Configuração do Postgres no n8n
Credencial PostgreSQL
- Host:
localhost(ou host do banco n8n) - Database:
n8n - User:
n8n - Password: senha do banco
IMPORTANTE: O usuário precisa ter permissão para:
- Ler a tabela
settings - Atualizar a tabela
credentials
Variáveis de Ambiente (.env.local)
N8N_UPDATE_CREDENTIAL_WEBHOOK=https://n8n.automatizase.com.br/webhook/update-google-credential
N8N_GOOGLE_CREDENTIAL_ID=Y9Izvj7XzExRNw2W
Fluxo Completo
- Usuário clica "Conectar" → Redireciona para Google OAuth
- Google autoriza → Callback
/auth/google/callback?code=... - Callback Next.js → Chama
/api/google-calendar/exchange-token - API Next.js:
- Troca
codepor tokens no Google - Envia POST para webhook n8n com tokens
- Troca
- Webhook n8n:
- Encripta dados com
pgp_sym_encrypt - Atualiza credencial no banco
- Retorna sucesso
- Encripta dados com
- Popup fecha → Credencial atualizada!
Segurança
Opção 1: Token Secreto
Adicionar header de autenticação:
// No Next.js:
headers: {
"Content-Type": "application/json",
"X-Webhook-Token": process.env.N8N_WEBHOOK_SECRET
}
// No n8n (IF node antes do Postgres):
{{ $('Webhook').item.json.headers['x-webhook-token'] === 'seu-token-secreto' }}
Opção 2: IP Whitelist
Configurar firewall para aceitar apenas IP do servidor Next.js.
⚠️ SOLUÇÃO ALTERNATIVA SIMPLES (RECOMENDADA)
Se você está tendo problemas com SQL/pgcrypto, existe uma solução muito mais simples:
Configurar OAuth Manualmente no n8n (1 vez)
- Acesse n8n UI → Credentials
- Edite a credencial
Y9Izvj7XzExRNw2W(Google Calendar OAuth2) - Clique em "Connect My Account"
- Complete o OAuth flow do Google
- Pronto! A credencial fica salva e o n8n renova os tokens automaticamente
Vantagens
✅ Simples: Não precisa SQL, pgcrypto, ou webhook
✅ Seguro: n8n gerencia encriptação automaticamente
✅ Auto-renovação: n8n renova access_token quando expira usando refresh_token
✅ Sem código: Apenas configuração via interface
Quando usar esta solução
- Se você encontrou qualquer erro de SQL acima
- Se não tem acesso superuser ao PostgreSQL
- Se prefere simplicidade e confiabilidade
- Se vai usar a mesma conta Google para todos os usuários
E o botão "Conectar" no portal?
Você pode:
Opção 1: Remover o botão (credencial já configurada)
Opção 2: Fazer o botão apenas verificar se está conectado:
const handleCheckConnection = async () => {
// Apenas verifica status via n8n
const response = await fetch('/api/google-calendar/status');
const { connected } = await response.json();
if (connected) {
alert('Google Calendar já está conectado!');
} else {
alert('Configure a credencial no n8n UI primeiro.');
}
};
Opção 3: Criar múltiplas credenciais no n8n (uma por usuário):
google-calendar-user-1google-calendar-user-2- etc.
E selecionar dinamicamente no workflow baseado em userId.
Troubleshooting
Erro: "relation 'credentials' does not exist"
Causa: n8n pode estar usando SQLite (padrão) ou schema diferente no PostgreSQL.
Solução 1: Verificar tipo de banco do n8n:
# Checar variável de ambiente
echo $DB_TYPE # sqlite ou postgresdb
# Ou verificar n8n config
cat ~/.n8n/config # procurar por database.type
Solução 2: Se for PostgreSQL, descobrir o schema e tabela:
-- Conectar no banco do n8n e executar:
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_name LIKE '%credentials%' OR table_name = 'settings';
Resultado esperado:
table_schema | table_name
-------------+-------------------
public | credentials_entity
public | settings
IMPORTANTE: n8n usa TypeORM, então:
- Tabelas:
credentials_entity,workflow_entity, etc. - Colunas:
updatedAt,createdAt(camelCase com aspas duplas)
Solução 3: Se n8n usa SQLite (banco padrão):
- NÃO é possível atualizar credenciais via SQL externo
- Use a abordagem manual: configure OAuth uma vez no n8n UI
Erro: function pgp_sym_encrypt does not exist
Causa: Extensão pgcrypto não está habilitada no PostgreSQL.
Solução: Habilitar a extensão (requer superuser):
-- Conectar como superuser (postgres)
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- Verificar se foi habilitada
\dx pgcrypto
Se não tiver permissão de superuser, NÃO é possível encriptar via SQL. Nesse caso, use a Solução Alternativa Simples abaixo.
Erro: "encryptionKey not found"
Verificar se existe a chave de encriptação:
SELECT * FROM public.settings WHERE key = 'encryptionKey';
Erro: "permission denied"
Conceder permissões ao usuário:
GRANT SELECT ON public.settings TO n8n;
GRANT UPDATE ON public.credentials TO n8n;
Erro: column "updated_at" does not exist (sugere "updatedAt")
Causa: n8n usa TypeORM que nomeia colunas em camelCase.
Solução: Use aspas duplas em colunas camelCase:
-- ERRADO
UPDATE credentials_entity SET updated_at = NOW() ...
-- CORRETO
UPDATE credentials_entity SET "updatedAt" = NOW() ...
Colunas comuns do n8n:
"updatedAt","createdAt"(com aspas!)id,name,type,data(sem aspas, são lowercase)
Erro: Parâmetros não substituídos ($1, $2 aparecem literalmente)
Causa: Formato incorreto dos parâmetros no Postgres Node.
Solução: Use o formato correto na aba Options:
- Query Parameters:
data,credentialId(sem símbolos $) - Query Parameters Values:
={{ $json.credentialDataJson }},={{ $json.credentialId }}
Ou use named parameters (:nome):
UPDATE public.credentials_entity
SET data = pgp_sym_encrypt(:data::text, ...), "updatedAt" = NOW()
WHERE id = :credentialId
Teste Manual
curl -X POST https://n8n.automatizase.com.br/webhook/update-google-credential \
-H "Content-Type: application/json" \
-d '{
"userId": "test-user",
"credentialId": "Y9Izvj7XzExRNw2W",
"credentialData": {
"clientId": "...",
"clientSecret": "...",
"oauthTokenData": { ... }
}
}'