Requisição HTTP com autenticação OAuth2 multi-tenant

Descreva o problema/erro/pergunta

Para acessar a API de terceiros, preciso usar OAuth2 para autenticar e obter um token. O processo é dividido em 3 etapas:
1 - Autenticar
2 - Converter código em token
3 - Usar token para acessar a API
Na etapa 1, a URL de autorização é estática, após autenticação/autorização ela retorna como resposta um código, mas também uma api_url que deve ser usada para converter o código de autorização em um Token durante a etapa 2.
Poderia me ajudar sobre como devo configurar a URL do Token de Acesso para usar este valor api_url + ‘/oauth2/access_token’ e também configurar a URL da solicitação HTTP?
Obrigado

Informações sobre sua configuração n8n

  • Versão n8n:
  • Banco de dados (padrão: SQLite):
  • Configuração n8n EXECUTIONS_PROCESS (padrão: own, main):
  • Executando n8n via (Docker, npm, n8n cloud, app desktop): bunx
  • Sistema operacional: ubuntu

Oi @tpirart, enquanto você aguarda uma resposta, aqui estão algumas coisas que podem ajudar:

Recursos sugeridos

Correspondência automática para sua pergunta.

Documentação:

Fórum:

@ricor, @hubschrauber, @Gallo_AIA - vocês já ajudaram com problemas similares, conseguem dar uma olhada?

Sugerido automaticamente pelo bot da comunidade n8n. É um piloto - compartilhe seu feedback aqui.

A versão resumida é que a credencial OAuth2 integrada não fará isso. As credenciais são resolvidas antes da execução do workflow, então a URL do Token de Acesso não pode depender de um valor que só existe após a etapa 1. Não há lugar para colocar a expressão.

Duas saídas, dependendo de quantos tenants você está tratando.

Se for uma mão cheia e a api_url de cada tenant é estável uma vez que você a viu, basta criar uma credencial OAuth2 por tenant com essa URL codificada como a URL do Token de Acesso e selecionar a credencial por execução. Não é elegante, mas são dez minutos de trabalho e você mantém o tratamento de refresh do n8n de graça.

Se for aberto, você precisa executar o fluxo você mesmo:

Use um nó Webhook como seu URI de redirecionamento, para que ele receba o código e a api_url.

HTTP Request para {{ $json.query.api_url }}/oauth2/access_token para fazer a troca.

Armazene o token de acesso, token de refresh e expiração indexados por tenant, em Postgres ou o que você já tiver. $getWorkflowStaticData(‘global’) funciona para um tenant, mas não é seguro quando as execuções se sobrepõem.

Para as próprias chamadas de API, use o Tipo de Credencial Genérico com Autenticação de Header, ou apenas defina o header Authorization a partir de uma expressão que extrai o token armazenado.

Uma coisa que vale a pena construir desde o início: verificar se seu provedor retorna um novo token de refresh a cada refresh. Muitos fazem. Se fizer e você continuar escrevendo o original de volta, cada tenant morre silenciosamente por volta da época em que esse primeiro token de refresh expira, e se apresenta como um problema de autenticação em vez de um problema de armazenamento. Escreva o novo token de refresh no momento em que a resposta chegar, na mesma etapa, não em um agendamento posterior.

Oi @mouadkommir,

Seu fluxo manual está correto. Uma pequena correção no motivo: as versões atuais do n8n podem resolver expressões dentro de campos de credenciais durante uma execução de fluxo:

Isso ainda não pode resolver essa conexão OAuth específica. Clicar em Conectar e lidar com o callback OAuth ocorrem fora de uma execução de fluxo, portanto não há contexto $json.query.api_url disponível. O n8n constrói o cliente OAuth usando a URL do Token de Acesso configurada e troca o código antes de armazenar parâmetros de callback extras como api_url.

A opção de credencial por locatário, portanto, só funciona quando o host do locatário já é conhecido antes de conectar. Com credenciais fixas normais, o nó HTTP Request relevante também deve estar ligado à credencial desse locatário.

Para locatários abertos, sua abordagem de Webhook mais armazenamento manual de token e atualização permanece a solução prática.

Muito obrigado pela sua explicação.

Vou testar as 2 abordagens e farei um retorno sobre minha experiência.

Obrigado por essa precisão … isso poderá ser útil em outro lugar :slight_smile:

A credencial padrão OAuth2 é provavelmente a restrição aqui. Ela espera uma URL de token fixa, portanto uma api_url retornada durante a autorização normalmente não pode ser injetada dinamicamente nessa credencial. Para uma implementação multi-tenant, eu separaria a troca em nós HTTP Request, armazenaria a URL da API retornada e os tokens de cada tenant, e então usaria esses valores para requisições subsequentes. Posso ajudar a mapear e testar o fluxo se for útil.

@Anshul_Namdev, você está certo, e obrigado pelo link. Verifiquei e as expressões em credenciais agora se resolvem em tempo de execução, então minha versão generalizada disso estava desatualizada.

Sua explicação é a mais útil mesmo assim. Connect e o callback acontecem fora de uma execução de fluxo de trabalho, portanto não há contexto de item para referenciar mesmo onde as expressões são suportadas. Essa distinção é o que causa confusão nas pessoas, porque “expressões funcionam em credenciais” é verdade e ainda assim não te leva lá.

A ressalva de credencial por inquilino também é uma observação válida. @tpirart, essa rota só funciona se você conhece a api_url antes de conectar. Se ela apenas retornar no momento da autorização, então você está no caminho do webhook quer goste ou não, então é isso que você deve verificar primeiro.

Se api_url for retornado apenas durante o callback de autorização, ele não poderá ser usado para configurar a URL do token de credencial OAuth2 padrão para essa mesma conexão. Use um Webhook como o URI de redirecionamento, troque o código com uma Requisição HTTP como {{ $json.query.api_url }}/oauth2/access_token e armazene tokens e expiração codificados por tenant. Uma credencial fixa separada por tenant é mais simples quando o host de cada tenant já é conhecido antes da autorização.

api_url são estáveis e não tenho muitos tenants, então escolhi a implementação estática.

Configurei a autenticação OAuth2 com o valor de api_url que obtive de uma primeira requisição.

Agora o processo de autenticação falhou antes de trocar o código recebido por um token.

1 - Obtenho a página de login do provedor de autorização.

2 - Preencho as credenciais para autenticar e recebo uma segunda página para aceitar ou recusar a autorização. Clico para autorizar o acesso.

3 - Recebo uma mensagem de erro dizendo o seguinte:

“Error: The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
More details
invalid_grant
Failed to connect. The window can be closed now.”

Quando comparo o valor do cabeçalho “state” (terminando com o caractere ‘=’) na URL das 3 etapas, parece haver uma codificação dupla na primeira etapa (…U1ODA2MX0%253D), na segunda (…U1ODA2MX0%3D) e na terceira etapa (…U1ODA2MX0=)

Em paralelo, criei um script Python para testar a autenticação OAuth2 e consegui chegar ao final e obter o token e refresh_token.

Poderia ser um problema na implementação OAuth2 no n8n?

Recebo algumas informações do provedor de autenticação que solicita que o grant_type = “auth_code” em vez de “authorization_code”. É possível personalizar a requisição OAuth2 para atender a este requisito?

Este é um fluxo OAuth2 não padrão (endpoint de token dinâmico), portanto a credencial OAuth2 integrada no n8n não vai lidar com isso de forma limpa.

Você precisa fazer o fluxo manualmente com nós HTTP Request.

Configuração recomendada

1. Etapa de autorização (Etapa 1)

Use um link normal ou um webhook que redirecione o usuário para a URL de Autorização estática.

2. Callback / Converter código em token (Etapa 2)

Depois que o usuário autorizar, o serviço vai redirecionar de volta para o seu webhook n8n com o código e api_url.

Em seguida, use um nó HTTP Request:

• Método: POST

• URL:

{{ $json.api_url }}/oauth2/access_token

• Corpo (geralmente form-urlencoded ou JSON, verifique a documentação da API):

{
“grant_type”: “authorization_code”,
“code”: “{{ $json.code }}”,
“client_id”: “your_client_id”,
“client_secret”: “your_client_secret”,
“redirect_uri”: “your_callback_url”
}

3. Armazenar ambos os valores

Salve em um banco de dados, Data Store ou dados estáticos:

• access_token

• refresh_token (se fornecido)

• api_url (muito importante)

4. Chamadas posteriores à API (Etapa 3)

Para cada solicitação à API de terceiros:

• URL: {{ $json.api_url }}/your-endpoint

• Autenticação: Header
Authorization: Bearer {{ $json.access_token }}