OAuth2 genérico não está atualizando

Oi a todos,

Estou tendo problemas para integrar com a API do Jobber. Consegui autenticar inicialmente e tenho alguns fluxos de trabalho funcionando, mas tudo quebra em 1 hora quando o access_token expira.

Jobber não exige escopos especiais de acordo com meus testes para retornar o refresh token, mas por algum motivo ele ou não está sendo retornado/armazenado, ou n8n não está lidando com isso corretamente.

Alguém tem sugestões?

aqui estão minhas informações de solução de problemas copiadas da issue que criei no GitHub

Aqui está a documentação deles - https://developer.getjobber.com/docs/building_your_app/app_authorization/

Consigo autorizar inicialmente, mas após a expiração de 1 hora recebo o seguinte erro

Saída
1 item
Falha na autorização - verifique suas credenciais
Tipo de conteúdo não suportado: text/plain; charset=utf-8
Detalhes do erro

De HTTP Request
Código de erro

401

Mensagem completa

Tipo de conteúdo não suportado: text/plain; charset=utf-8
Requisição

{ “hidden”: “{\n “query”: “{ quotes(first: 10, filter: { status: converted}) { nodes { id quoteNumber notes(first: 5) { nodes { … on QuoteNote { message } } } } } }”\n}”, “headers”: { “content-type”: “application/json”, “x-jobber-graphql-version”: “2025-04-16”, “accept”: “application/json,text/html,application/xhtml+xml,application/xml,text/;q=0.9, image/;q=0.8, /;q=0.7”, “Authorization”: “hidden” }, “method”: “POST”, “uri”: “https://api.getjobber.com/api/graphql”, “gzip”: true, “rejectUnauthorized”: true, “followRedirect”: true, “resolveWithFullResponse”: true, “sendCredentialsOnCrossOriginRedirect”: false, “followAllRedirects”: true, “timeout”: 300000, “encoding”: null, “json”: false, “useStream”: true }
Outras informações
Índice do item

0

Tipo de nó

n8n-nodes-base.httpRequest

Versão do nó

4.4 (Mais recente)

Versão do n8n

2.20.6 (Auto-hospedado)

Hora

5/12/2026, 9:52:03 AM

Rastreamento de pilha

NodeApiError: Authorization failed - please check your credentials at ExecuteContext.execute (/usr/local/lib/node_modules/n8n/node_modules/.pnpm/n8n-nodes-base@file+packages+nodes-base_@aws-sdkaws-sdkaws-sdkaws-sdk+credential-providers@3.808.0_asn1.js@5_8da18263ca0574b0db58d4fefd8173ce/node_modules/n8n-nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts:825:16) at WorkflowExecute.executeNode (/usr/local/lib/node_modules/n8n/node_modules/.pnpm/n8n-core@file+package@opent@openlemetry+core_@open@opentelemetrye@opentelemetryemetry+api@1.9.0_@opentelemetry+exporter-trace-otlp_2c2e1f47b69b34bef6f634a13cbf61d9/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:1048:9) at WorkflowExecute.runNode (/usr/local/lib/@opentelemetrynpmode_modules/n8n/node_modules/.@opentelemet@opentelemetryynpm/n8n-co@opentelemetry@opentelemetry@file+packages+core_@opentelemetry+api@1.9.0_@opentelemetry+exporter-trace-otlp_2c2e1f47b69b34bef6f634a13cbf61d9/node_modules/n8n-core/@opentelemetryodulesrc/ex@opentelemetryode_modulescution-engine/workflow-execute.ts:1@opentelemetry39:11) at /@opentelemetrysr/local/lib/node_@opentelemetryodules/n8n/@opentelemetryode_modules/.pnpm/n8n-core@file+packages+core_@opentelemetry+api@@opentelemetry7.9.0_@opentelemetry+exporter-trace-otlp_2c2e1f47b69b34bef6f634a13cbf61d9/node_modules/n8n-core/s@opentelemetryc/execution@opentelemetryengine/workflow-execute.ts:1687:@opentelemetry7 at /usr/l@opentelemetrycal/lib/node_modules/n8n/node_modules/.pnpm/n8n-core@file+packages+core_@opentelemetry+api@1.9.0_@opentelemetry+exporter-trace-otlp_2c2e1f47b69b34bef6f634a13cbf61d9/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:2339:11
Minha compreensão é que a resposta 401 deveria disparar uma atualização de token, mas isso não parece estar acontecendo. Reiniciar o fluxo de trabalho também não ajuda.

Se eu for para credenciais posso clicar em “reconectar” o que permitirá que as execuções subsequentes do fluxo de trabalho funcionem por uma hora.

Para reproduzir
Configure a credencial Generic OAuth2 com authorization_code contra um provedor que rotaciona refresh tokens.
Execute o fluxo de trabalho com sucesso.
Aguarde a expiração do access token.
Observe o ciclo de atualização; após o ciclo subsequente, a autenticação falha exigindo reconexão.
Comportamento esperado
n8n deve persistir e usar o refresh_token rotacionado mais recente da resposta de atualização de token.
Os fluxos de trabalho devem continuar sem reconexão manual.

Informações de depuração
Informações de depuração
núcleo
n8nVersion: 2.20.6
platform: docker (auto-hospedado)
nodeJsVersion: 24.14.1
nodeEnv: production
database: sqlite
executionMode: regular
concurrency: -1
license: enterprise (production)
consumerId: 268c9581-f4d9-44ed-a7be-25c5c834d114
armazenamento
success: all
error: all
progress: false
manual: true
binaryMode: filesystem
limpeza
enabled: true
maxAge: 336 hours
maxCount: 10000 executions
client
userAgent: mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/148.0.0.0 safari/537.36
isTouchDevice: false
cluster
instanceCount: 1
versions: 2.20.6
instances:
instanceKey: 0cffc1ac-43d6-4469-85cf-116816732522, hostId: main-dec161f067e6, instanceType: main, instanceRole: leader, version: 2.20.6
checks:
check: hostid-clash, status: succeeded, warnings: -
check: lifecycle, status: succeeded, warnings: -
check: split-brain, status: succeeded, warnings: -
check: version-mismatch, status: succeeded, warnings: -
Generated at: 2026-05-12T17:00:18.159Z

Sistema Operacional
Ubuntu 22.04 LTS

Versão do n8n
2.20.6

Versão do Node.js
qualquer uma que estiver na imagem: docker.n8n.io/n8nio/n8n

Banco de dados
SQLite (padrão)

Modo de execução
main (padrão)

Hosting
auto-hospedado

Bem-vindo @oxidation0917 à nossa comunidade! Sou Jay e sou um criador verificado n8n.

Este é um bug conhecido do Generic OAuth2 - o refresh token não está sendo armazenado ou usado corretamente em alguns casos. Um workaround prático enquanto aguarda a correção: na credencial Generic OAuth2, certifique-se de que “Authentication” está definido como “Body” (não Header) se o Jobber suportar, e verifique se a URL do token está correta e inclui as credenciais do cliente necessárias no body. Se os tokens de acesso do Jobber têm vida curta (1 hora), você também pode criar um fluxo de refresh manual: um workflow agendado que é executado a cada 55 minutos, chama o endpoint de token do Jobber via HTTP Request com grant_type: refresh_token e atualiza a credencial via API REST do n8n. Isso o mantém operacional enquanto o bug é resolvido.

Oi @nguyenthieutoan Muito obrigado por dar uma olhada.

Atualmente estou usando Body nas configurações de autenticação, pois é a única forma que funciona. Quando você diz verificar se o Jobber inclui as credenciais necessárias no corpo, você quer dizer verificar o corpo de um curl?

Eu estava pensando na abordagem que você sugere, usando um gatilho cron, mas não consegui mapear bem na minha cabeça e pela documentação. Há uma forma de acessar as credenciais armazenadas (especificamente refresh_token) na requisição HTTP? Entendo a parte de atualizar a credencial via API (vou ter que descobrir a estrutura), mas como a requisição requer o refresh_token, fiquei preso em como acessá-lo.

Oi @oxidation0917, fico feliz em esclarecer isso um pouco mais.

  1. Sobre “credenciais no corpo”
    Quando mencionei verificar se o Jobber inclui as credenciais necessárias no corpo, quis dizer: compare o que você envia do n8n com um exemplo curl funcional da documentação do Jobber ou de seus próprios testes. Por exemplo, em uma chamada típica de atualização OAuth2, o corpo geralmente precisa de campos como:
  • grant_type=refresh_token

  • refresh_token=<seu_refresh_token>

  • client_id=<seu_client_id>

  • client_secret=<seu_client_secret>

Se seu exemplo curl funciona, mas o nó HTTP Request do n8n falha, você pode espelhar o mesmo corpo e headers exatos do curl para o nó.

  1. Acessando o refresh_token armazenado
    Infelizmente, com o bug atual do Generic OAuth2, o n8n não está expondo o refresh_token armazenado de forma fácil dentro dos nós normais. É exatamente por isso que a atualização automática está quebrada. Então, em vez de tentar “ler” o refresh token da credencial em tempo de execução, a solução habitual é:
  • Armazene o refresh_token em um local que você possa controlar, por exemplo em:

    • Uma variável do n8n (Variável de ambiente), ou

    • Um banco de dados/tabela separado, ou

    • Um armazenamento de dados simples como PostgreSQL/Firestore/Notion, dependendo do seu stack.

  • Então seu workflow programado pode:

    • Ler esse refresh_token armazenado

    • Chamar o endpoint de token do Jobber com grant_type=refresh_token

    • Atualizar o token de acesso no n8n via REST API

  1. Esboço do fluxo de atualização manual
    Muito basicamente, o workflow seria algo assim:
  • Nó Cron: executado a cada 55 minutos

  • (Opcional) Nó para buscar o refresh_token armazenado mais recente do seu armazenamento

  • Nó HTTP Request:

    • Método: POST

    • URL: URL de token do Jobber

    • Autenticação: nenhuma (porque você envia tudo no corpo)

    • Corpo: grant_type=refresh_token, refresh_token=..., client_id, client_secret, etc.

  • Nó HTTP Request (API n8n):

    • Método: PATCH

    • URL: https://<sua-url-n8n>/rest/credentials/<id-credencial>

    • Autenticação: use sua autenticação de API n8n

    • Corpo: atualize o accessToken (e opcionalmente o refresh token se o Jobber retornar um novo)

Se desejar, posso montar um exemplo JSON concreto tanto para a solicitação de token do Jobber quanto para o payload de atualização de credencial do n8n, para que você possa colocar diretamente na sua instância.

Parece que o n8n pode não estar armazenando o refresh_token após o fluxo OAuth inicial. Eu verificaria a resposta completa do token do Jobber e confirmaria se o refresh token está sendo retornado e persistido. Às vezes, os provedores só retornam isso na primeira solicitação de autorização.

Você também poderia tentar forçar parâmetros de acesso offline / consentimento na configuração OAuth. Já que você já abriu uma issue no GitHub em GitHub, compartilhar a resposta bruta do token (com segredos removidos) provavelmente ajudaria a estreitar rapidamente.

@nguyenthieutoan Obrigado! Isso é ótimo. Estou bem encaminhado, escrevendo um passo a passo para a posteridade, porém estou tendo problemas para atualizar via API.

Solução de problemas que não funcionou

Inicialmente tentei:

{
  "data": {
    "oauthTokenData": {
      "access_token": "access_token",
      "refresh_token": "refresh_token"
    }
  }
}

Mas recebi

{
  "message": "request.body.data does not match allOf schema [subschema 0] with 12 error[s]:,request.body.data does not match allOf schema [subschema 0] with 1 error[s]:,request.body.data requires property \"grantType\",request.body.data does not match allOf schema [subschema 1] with 1 error[s]:,request.body.data requires property \"accessTokenUrl\",request.body.data does not match allOf schema [subschema 2] with 1 error[s]:,request.body.data requires property \"clientId\",request.body.data does not match allOf schema [subschema 3] with 1 error[s]:,request.body.data requires property \"clientSecret\",request.body.data does not match allOf schema [subschema 4] with 1 error[s]:,request.body.data requires property \"scope\",request.body.data does not match allOf schema [subschema 5] with 1 error[s]:,request.body.data requires property \"authentication\",request.body.data does not match allOf schema [subschema 1] with 2 error[s]:,request.body.data does not match allOf schema [subschema 0] with 1 error[s]:,request.body.data requires property \"serverUrl\",request.body.data does not match allOf schema [subschema 2] with 4 error[s]:,request.body.data does not match allOf schema [subschema 0] with 1 error[s]:,request.body.data requires property \"authUrl\",request.body.data does not match allOf schema [subschema 1] with 1 error[s]:,request.body.data requires property \"authQueryParameters\",request.body.data does not match allOf schema [subschema 3] with 4 error[s]:,request.body.data does not match allOf schema [subschema 0] with 1 error[s]:,request.body.data requires property \"sendAdditionalBodyProperties\",request.body.data does not match allOf schema [subschema 1] with 1 error[s]:,request.body.data requires property \"additionalBodyProperties\",request.body.data does not match allOf schema [subschema 4] with 2 error[s]:,request.body.data does not match allOf schema [subschema 0] with 1 error[s]:,request.body.data requires property \"jwksUriNotice\""
}

Tentei fornecê-lo, e depois de algumas iterações com o Claude acabei com isto:

{
  "data": {
    "grantType": "authorizationCode",
    "clientId": "clientId",
    "clientSecret": "clientSecret",
    "accessTokenUrl": "https://api.getjobber.com/api/oauth/token",
    "authUrl": "https://api.getjobber.com/api/oauth/authorize",
    "serverUrl": "{{MY N8N HOST?}}"
    "authQueryParameters": "",
    "scope": "",
    "authentication": "body",
    "jweEnabled": false,
    "oauthTokenData": {
      "access_token": "access_token",
      "refresh_token": "refresh_token",
      "token_type": "Bearer"
    }
  }
}

que aparentemente passa nas verificações de schema, mas agora recebo um erro 500. Não sei o que deveria estar em serverURL, então coloquei meu host n8n.

Código	Detalhes
500
Não documentado
Erro: Internal Server Error

Corpo da resposta
Baixar





Erro




Internal Server Error




Esta é minha estrutura de credenciais do banco de dados que não possui nenhum dos parâmetros adicionais sendo exigidos pela API. @David_Warner, parece que o n8n está armazenando refresh_token.

{
    "authUrl": "https://api.getjobber.com/api/oauth/authorize",
    "accessTokenUrl": "https://api.getjobber.com/api/oauth/token",
    "clientId": "clientId",
    "clientSecret": "clientSecret",
    "scope": "offline_access",
    "authentication": "body",
    "oauthTokenData": {
        "access_token": "access_token",
        "refresh_token": "refresh_token",
        "token_type": "Bearer"
    }
}

Aguarde. Consegui atualizar via API depois de brincar com o Request_Body. Vou compartilhar os detalhes em breve.

Edição - Aqui está o que documentei até agora.

Um grande obrigado a @nguyenthieutoan por me colocar no caminho certo. Fiz muito progresso.

Esperando tornar isso mais fácil para outra pessoa (e para mim) no futuro, passei por todos os passos de autenticação novamente, documentando conforme avançava.

Autenticação Inicial / Teste

  1. URL de Autorização - Digite no navegador que está autenticado com Jobber. A URL para a qual você será redirecionado contém o código de autorização e o estado como confirmação.
https://api.getjobber.com/api/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https:/YOUR_HOST/rest/oauth2-credential/callback&state=abc123

Resposta que recebi:

https://n8n.lab.atreehuman.com/rest/oauth2-credential/callback?code=CODE&state=abc123
  1. Curl com código de autorização - Ajuste este CURL com seu code da URL anterior
curl -X POST https://api.getjobber.com/api/oauth/token -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=authorization_code&code=CODE&redirect_uri=YOUR_HOST/rest/oauth2-credential/callback"

Resposta:

{"access_token":"ACCESS_TOKEN","refresh_token":"REFRESH_TOKEN"}%

Você está autenticado e tem 1 hora para atualizar seu access_token antes de expirar. Seu refresh_token deve funcionar após isso (indefinidamente até rotação?). Por padrão, a API Jobber rotaciona refresh_token a cada atualização do seu access_token. Você deve armazenar seu refresh_token em algum lugar acessível para sua atualização.

  1. Teste de Autorização de API:
curl -X POST -H "Authorization: Bearer ACCESS_TOKEN" "https://api.getjobber.com/api/graphql"

Resposta quando Autorizado:

{"message":"An API version must be specified"}%

Atualizando Credenciais n8n

  1. Crie uma chave de API em Settings > n8n API com escopos de credential:list, credential_update e credential:read. Salve a chave mostrada no pop-up após ambos para sua área de transferência e um local seguro. Você precisará dela mais tarde ao criar seu workflow de cron para atualizar.

  2. Abra o API Playground e autorize com sua chave de API. Descobri que precisei atualizar a página após a autenticação para que a autenticação entrasse em vigor.

  3. Role para baixo até GET /credentials, clique e “Try it out”

  4. Encontre sua credencial Jobber nos dados de resposta e salve seu id no seu bloco de notas e área de transferência

  5. Procure mais adiante e encontre PATCH /credentials/id, clique em “Try it out” e cole seu id.

  6. Dentro de “Request Body” cole o seguinte e atualize com seus dados. (refresh_token não precisa realmente estar aqui já que n8n não está usando)

{
    "data": {
        "grantType": "authorizationCode",
        "serverUrl": "",
        "jweEnabled": false,
        "clientId": "CLIENT_ID",
        "clientSecret": "CLIENT_SECRET",
        "accessTokenUrl": "https://api.getjobber.com/api/oauth/token",
        "authUrl": "https://api.getjobber.com/api/oauth/authorize",
        "scope": "",
        "authentication": "body",
        "authQueryParameters": "",
        "oauthTokenData": {
            "access_token": "ACCESS_TOKEN",
            "refresh_token": "REFRESH_TOKEN"
        }
    }
}

Você deve receber um Código 200 com corpo de resposta:

{
    "id": "ID",
    "name": "Jobber",
    "type": "oAuth2Api",
    "isManaged": false,
    "isGlobal": false,
    "isResolvable": false,
    "resolvableAllowFallback": false,
    "resolverId": null,
    "createdAt": "2026-05-09T17:53:07.738Z",
    "updatedAt": "2026-05-16T19:27:53.231Z"
}

Agora tudo o que resta é mover seu refresh_token para um local seguro que possa ser acessado de um workflow, e então criar um workflow que atualize o token e atualize a API.

Atualizando seu access_token

curl -X POST https://api.getjobber.com/api/oauth/token -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=refresh_token&refresh_token=REFRESH_TOKEN"

Resposta:

{"access_token":"ACCESS_TOKEN","refresh_token":"REFRESH_TOKEN"}%

Apenas pegue isso e atualize suas credenciais com a API como acima, e você deve estar pronto. Agora tudo isso só precisa ser transformado em um workflow e um remendo aplicado!

So, I’m still hoping to get this issue resolved, but for now we can workaround it.

Here’s a workflow I came up with. You’d have to set up a Postgres database / credentials and point those nodes at it as well as set an error reporting workflow, or disable that flag.

I don’t really like storing the credentials in the database, but currently it’s the most practical approach without a self-hosted enterprise license. Any suggestions otherwise that are more secure?

I’m open to any suggestions on the workflow too. Thanks all!

Editar - Desmarcando como solução.

Bem, é meio que uma solução. Faz a autenticação durar mais tempo. Talvez um dia agora, mas parece que n8n ESTÁ atualizando o token, apenas em seu próprio cronograma. Isso acaba quebrando o remendo.

É possível que isso também afete credenciais OAuth2 do GitHub, não apenas credenciais OAuth2 genéricas? Estou usando uma credencial OAuth2 do GitHub junto com nós do GitHub para buscar informações. Recentemente tenho recebido um erro 401:
{ "message": "Bad credentials", "documentation_url": "https://docs.github.com/rest", "status": "401" }

Consigo resolver isso manualmente clicando no botão “reconectar” na página de credenciais. Mas essa solução alternativa funciona apenas por algumas horas.

Estou tendo o mesmo problema com um nó de API OAuth2 do MCP. Não consegue atualizar o token. Há alguma novidade sobre o conserto?

Duas coisas a verificar que costumam ser esquecidas: primeiro, certifique-se de que o campo “Refresh URL” na credencial OAuth2 genérica está explicitamente definido para o endpoint de token do seu provedor - o n8n não inferirá a partir da “Access Token URL” mesmo que geralmente sejam iguais. Segundo, verifique se sua solicitação de autorização inicial inclui access_type=offline (ou prompt=consent para provedores baseados em Google) - sem isso, muitos provedores não emitem um token de atualização, então o n8n não tem nada para usar quando o token de acesso expira. Você pode adicionar esses parâmetros em “Auth URI Query Parameters” nas configurações de credencial.

Obrigado pela resposta, @nguyenthieutoan. Sim, eu já tentei os dois antes, mas não consegue atualizar depois de 1 hora ou mais.

Quanto à URL, sim, preenchie ambas, Auth URL e Access token URL,

tem mais algum passo de diagnóstico?

Obrigado,

Para o Google especificamente, o token de atualização é emitido apenas uma vez - na primeira autorização. Se você autorizou sem access_type=offline e prompt=consent no campo “Auth URI Query Parameters”, o Google não retornará um token de atualização, e nenhuma quantidade de ajuste de URL vai consertar isso. Tente isto: na sua credencial, em Auth URI Query Parameters adicione access_type=offline e prompt=consent, depois revogue e re-autorize a credencial do zero. Isso deve fazer com que o Google emita um novo token de atualização.

Obrigado, parece que funcionou.

Não sabia como enviar os dois, mas foi simples, "access_type=offline&prompt=consent"