- 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.
191 lines
5.1 KiB
Markdown
191 lines
5.1 KiB
Markdown
# Webhook n8n para Gerar OAuth URL
|
|
|
|
## Problema
|
|
|
|
O endpoint REST `/rest/oauth2-credential/auth` do n8n requer cookies de sessão (usuário logado), não aceita API Key.
|
|
|
|
## Solução
|
|
|
|
Criar um workflow no n8n com webhook que gera a `oauth_url` internamente.
|
|
|
|
## Workflow n8n
|
|
|
|
### 1. Webhook (Trigger)
|
|
- **Path**: `/webhook/google-calendar-oauth-url`
|
|
- **Method**: `GET` ou `POST`
|
|
- **Authentication**: `None` (webhook público)
|
|
|
|
### 2. Code Node (Gerar oauth_url)
|
|
|
|
```javascript
|
|
// Configuração
|
|
const credentialId = "ccTThTS9TKsejR7W"; // Seu credential ID
|
|
const clientId = "174466774807-xxx.apps.googleusercontent.com"; // Google Client ID
|
|
const callbackUrl = $json.query?.callbackUrl || "http://localhost:3000/api/google-calendar/callback";
|
|
|
|
// Gerar CSRF token (aleatório)
|
|
const csrfToken = Math.random().toString(36).substring(2, 15) +
|
|
Math.random().toString(36).substring(2, 15);
|
|
|
|
// Criar state (formato do n8n)
|
|
const state = {
|
|
token: csrfToken,
|
|
cid: credentialId,
|
|
createdAt: Date.now()
|
|
};
|
|
|
|
// Codificar state em base64
|
|
const stateBase64 = Buffer.from(JSON.stringify(state)).toString('base64');
|
|
|
|
// Construir oauth_url
|
|
const oauthUrl = new URL("https://accounts.google.com/o/oauth2/v2/auth");
|
|
oauthUrl.searchParams.set("client_id", clientId);
|
|
oauthUrl.searchParams.set("redirect_uri", callbackUrl);
|
|
oauthUrl.searchParams.set("response_type", "code");
|
|
oauthUrl.searchParams.set("scope", "https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events");
|
|
oauthUrl.searchParams.set("access_type", "offline");
|
|
oauthUrl.searchParams.set("prompt", "consent");
|
|
oauthUrl.searchParams.set("state", stateBase64);
|
|
|
|
// Retornar
|
|
return {
|
|
json: {
|
|
oauthUrl: oauthUrl.toString(),
|
|
state: stateBase64,
|
|
credentialId: credentialId
|
|
}
|
|
};
|
|
```
|
|
|
|
### 3. Respond to Webhook
|
|
|
|
Retorna o resultado:
|
|
```json
|
|
{
|
|
"oauthUrl": "https://accounts.google.com/o/oauth2/v2/auth?...",
|
|
"state": "eyJ0b2tlbiI6InhjZDExMzkyLWUyMzkiLCJjaWQiOiJjY1RUaFRTOVRLc2VqUjdXIiwiY3JlYXRlZEF0IjoxNzU5OTUzNTY1MDI0fQ==",
|
|
"credentialId": "ccTThTS9TKsejR7W"
|
|
}
|
|
```
|
|
|
|
## Configuração no Dashboard
|
|
|
|
Atualize `.env.local`:
|
|
|
|
```bash
|
|
# Webhook do n8n para gerar oauth_url
|
|
N8N_OAUTH_URL_WEBHOOK=https://n8n.automatizase.com.br/webhook/google-calendar-oauth-url
|
|
```
|
|
|
|
Remova (não é mais necessário):
|
|
```bash
|
|
# N8N_API_KEY não é mais necessário
|
|
```
|
|
|
|
## Atualizar API Route
|
|
|
|
`app/api/google-calendar/auth/route.ts`:
|
|
|
|
```typescript
|
|
export async function GET(_request: NextRequest) {
|
|
try {
|
|
const webhookUrl = process.env.N8N_OAUTH_URL_WEBHOOK;
|
|
const callbackUrl = `${process.env.NEXT_PUBLIC_SITE_URL}/api/google-calendar/callback`;
|
|
|
|
if (!webhookUrl) {
|
|
return NextResponse.json(
|
|
{ error: "N8N_OAUTH_URL_WEBHOOK not configured" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
// Chamar webhook do n8n
|
|
const response = await fetch(
|
|
`${webhookUrl}?callbackUrl=${encodeURIComponent(callbackUrl)}`
|
|
);
|
|
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
console.error("n8n webhook error:", error);
|
|
return NextResponse.json(
|
|
{ error: "Failed to generate OAuth URL" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
return NextResponse.json({
|
|
oauthUrl: data.oauthUrl
|
|
});
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
return NextResponse.json(
|
|
{ error: "Internal server error" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Validação do State no n8n
|
|
|
|
**IMPORTANTE:** O n8n vai validar o `state` quando receber o callback do Google. Como estamos gerando o state manualmente, precisamos **desabilitar a validação CSRF** ou **salvar o token** no n8n.
|
|
|
|
### Opção 1: Desabilitar Validação (Mais Simples)
|
|
|
|
O n8n não valida CSRF token por padrão quando o callback vem pelo redirect do Google. Apenas valida o `cid` (credential ID).
|
|
|
|
### Opção 2: Salvar Token (Mais Seguro)
|
|
|
|
Adicionar um nó no workflow que salva o `csrfToken` temporariamente (Redis, banco, etc) e validar no callback.
|
|
|
|
## Fluxo Completo
|
|
|
|
```
|
|
1. Dashboard → GET /api/google-calendar/auth
|
|
↓
|
|
2. API Route → GET https://n8n.automatizase.com.br/webhook/google-calendar-oauth-url
|
|
↓
|
|
3. n8n Workflow gera state + oauth_url
|
|
↓
|
|
4. Dashboard redireciona para oauth_url
|
|
↓
|
|
5. Google → Callback http://localhost:3000/api/google-calendar/callback?code=...&state=...
|
|
↓
|
|
6. Dashboard salva no Supabase
|
|
↓
|
|
7. Dashboard → https://n8n.automatizase.com.br/rest/oauth2-credential/callback?code=...&state=...
|
|
↓
|
|
8. n8n processa OAuth e salva tokens
|
|
↓
|
|
9. Sucesso! ✅
|
|
```
|
|
|
|
## Vantagens
|
|
|
|
✅ Webhook público (sem autenticação)
|
|
✅ n8n controla geração do state
|
|
✅ Código simples e direto
|
|
✅ Fácil de debugar
|
|
|
|
## Alternativa: State Simplificado
|
|
|
|
Se o n8n não validar o CSRF token, podemos usar um state mais simples:
|
|
|
|
```javascript
|
|
const state = credentialId; // Apenas o credential ID
|
|
```
|
|
|
|
Teste primeiro para ver se o n8n aceita.
|
|
|
|
## Teste
|
|
|
|
```bash
|
|
# Testar webhook
|
|
curl "https://n8n.automatizase.com.br/webhook/google-calendar-oauth-url?callbackUrl=http://localhost:3000/api/google-calendar/callback"
|
|
|
|
# Deve retornar:
|
|
# {"oauthUrl":"https://accounts.google.com/...","state":"...","credentialId":"..."}
|
|
```
|