Body-Parameter für Refresh-Token-Anfrage im OAuth-Flow

Ich versuche, mich mit einer externen API über den OAuth-Flow mit dem Credential-Files-Ansatz für einen benutzerdefinierten Node zu verbinden. Das Problem, das ich habe, ist, dass ich während der Token-Aktualisierung auf dem OAuth-Server den Resource-Parameter als Body-Parameter senden muss, um einen ordnungsgemäß aktualisierten Zugriffstoken zu erhalten.

Hat jemand ein solches Szenario bereits bewältigt? Die standardmäßigen Body-Parameter reichen nicht aus.

@shamika der Standard-OAuth2-Credential-Helper in n8n bietet keine Anpassung des Refresh-Body out of the box, aber es gibt je nach Zielserver mehrere Wege.

Wenn es speziell Microsoft/Azure AD ist (häufigster Fall für die Notwendigkeit des Resource-Body-Parameters), ist die sauberere langfristige Lösung, von v1.0 auf v2.0-Endpoints zu wechseln — Microsoft hat den Resource-Parameter vor Jahren deprecated und durch Scope-with-Resource-baked-in ersetzt. Statt Body resource=https://graph.microsoft.com wird dein Scope zu https://graph.microsoft.com/.default. Der v2.0-Endpoint ist /oauth2/v2.0/token statt /oauth2/token. Keine Custom-Code nötig, wenn du die Endpoints wechseln kannst.

Wenn du die Endpoints nicht wechseln kannst (manche Enterprise-/Legacy-OAuth-Server brauchen resource wirklich als Body), ist der Weg für einen Custom-Node, NOT oAuth2Api zu erweitern, sondern stattdessen einen eigenen Credential-Type mit IAuthenticateGeneric zu definieren und den Refresh manuell zu handhaben:

{
  "name": "myCustomOAuth2Api",
  "displayName": "My Custom OAuth2",
  "properties": [
    {"displayName": "Client ID", "name": "clientId", "type": "string"},
    {"displayName": "Client Secret", "name": "clientSecret", "type": "string", "typeOptions": {"password": true}},
    {"displayName": "Resource", "name": "resource", "type": "string"}
  ],
  "authenticate": {
    "type": "generic",
    "properties": {
      "headers": {"Authorization": "=Bearer {{ $credentials.accessToken }}"}
    }
  }
}

dann im Node den Token-Refresh manuell über den preSend-Hook implementieren, indem du das Ablaufdatum checkst und mit deinen erforderlichen Body-Parametern (inklusive Resource) an die Token-URL POSTst. Erhöht die Komplexität, aber gibt dir volle Kontrolle über den Refresh-Flow.

drittes Option, wenn du oAuth2Api erweitern willst und nur Resource benötigst — versuch, es als Query-Parameter auf der accessTokenUrl selbst hinzuzufügen, n8ns Standard-Flow leitet Query-Parameter auch bei Refresh-Requests durch. zB accessTokenUrl: https://login.example.com/token?resource=https://api.example.com. Weniger sauber, aber der kleinste mögliche Patch.

Welcher OAuth-Server? Azure AD, Salesforce, Ping Identity, Custom Enterprise? Ändert, welcher Weg am realistischsten ist.

@achamm hat die Hauptwege gut abgedeckt. Eine praktische Ergänzung: Bevor du die Route IAuthenticateGeneric gehst, bestätige zunächst, welchen OAuth-Server du anstrebst. Wenn es ein moderner Server ist (Azure AD, Google, Okta), ist der scope-basierte v2-Endpoint fast immer die richtige Wahl und erfordert keinen benutzerdefinierten Code. Wenn es ein Legacy- oder On-Premise-OAuth-Server ist, der resource als Body-Parameter wirklich benötigt – das ist der Punkt, wo IAuthenticateGeneric + manueller preSend-Hook die sauberste Lösung ist. Kannst du teilen, welcher Service/OAuth-Server du verbindest? Das wird helfen, den richtigen Weg zu wählen.

Hallo @achamm, danke für die ausführliche Antwort – der OAuth-Server ist ein benutzerdefinierter, selbst gehosteter Server eines Clients. Um den Kontext zu geben: Das Access-Token ist ein JWT. Bei der Aktualisierung habe ich, da keine Ressource angegeben ist, ein undurchsichtiges Token statt eines JWT erhalten. Aber bei der anfänglichen Code-Austauschanfrage konnte ich über den Parameter ‘sendAdditionalBodyProperties’ ein JWT erhalten.

Ich habe Postman verwendet, um manuell ein Refresh-Token mit dem Parameter “resource” im Body abzurufen, und dann erhielt ich ein perfektes JWT. Das bestätigt, dass der Parameter “resource” den Unterschied ausmacht.

Ein undurchsichtiges Token kommt nicht in Frage, da der zu entwickelnde Service serverlos sein wird und die Authentifizierung in sich geschlossen sein muss. Ich würde hier eine saubere Lösung bevorzugen, da ich das für den Client pflegen muss.

Ich erwäge zwei Optionen: Die Query-Parameter-Option, wobei ich noch nicht sicher bin, ob der Server diese unterstützt. Die andere ist die benutzerdefinierte Refresh-Logik. Hast du schon einen Node gesehen, der einen solchen benutzerdefinierten Refresh-Token-Mechanismus implementiert hat, als Referenz?

@shamika die sauberste existierende Referenz ist der Community Node n8n-nodes-azure-openai-ms-oauth2 auf npm — er implementiert genau dieses Pattern (custom MS OAuth Refresh mit Resource Body Parameter) dadurch, dass er NICHT von oAuth2Api erbt und stattdessen den Token-Lebenszyklus im preSend Hook des Nodes verwaltet. Für deinen custom-server Flow sind es ungefähr ~50 Zeilen TypeScript: accessToken/refreshToken/expiresAt in der Credential speichern, Ablauf in preSend überprüfen, bei Ablauf mit Resource Body zu deinem Token Endpoint POSTen, den gecachten Token aktualisieren bevor die eigentliche Request weitergeleitet wird.

es lohnt sich, zuerst die Query-Parameter Route zu versuchen, da sie null Code benötigt — n8n’s Standard OAuth2Api Flow leitet URL Query Parameter tatsächlich an die Refresh Request durch, also wenn du accessTokenUrl auf https://ur-server/token?resource=https://api.target.com setzt und dein OAuth Server akzeptiert resource aus Body oder Query, funktioniert das einfach ohne irgendetwas zu forken. hängt ganz davon ab, ob dein Server strict body-only ist.

wenn es strict body-only ist, ist der n8n-nodes-azure-openai-ms-oauth2 Source der Blueprint — fork ihn, swap die MS-spezifischen Endpoints/Scopes gegen die deines Servers, deploy als privaten Community Node. viel sauberer langfristig als ein Code Node Hack.

willkommen in der n8n-community @shamika
könntest du bitte dein json ohne die sensiblen daten teilen ?

Ich habe das schon genau so gemacht. achamm’s Empfehlung von n8n-nodes-azure-openai-ms-oauth2 ist die sauberste Referenz, wenn du einen Custom Node baust — es verwaltet den gesamten Token-Lebenszyklus im preSend Hook des Nodes und fügt den Resource-Body-Parameter bei der Aktualisierung genau so hinzu, wie du es brauchst. Der Source ist auf npm und GitHub, leicht zu forken.

Wenn du eine noch einfachere Starter-Template sehen möchtest, hat der eingebaute n8n Google Drive Node eine Custom-OAuth2-Logik in seiner Credential-Definition, die Token-Refresh mit zusätzlichen Parametern verwaltet (sie ist im n8n Core Repo unter packages/nodes-base/credentials/GoogleOAuth2Api.credentials.ts). Das Muster ist fast identisch: Tokens speichern, Ablauf prüfen, zum Token-Endpoint mit den zusätzlichen Body-Feldern POSTen, die Access-Token aktualisieren, bevor die eigentliche Anfrage rausgeht.

Für deinen spezifischen Fall — Custom OAuth Server, brauche Resource im Body — würde die preSend-Logik ungefähr so aussehen (in der execute oder preSend Methode deines Custom Nodes):

async preSend(request, options) {

const credentials = await this.getCredentials(‘myCustomOAuth2Api’);

// Check if token is expired

if (Date.now() > credentials.expiresAt) {

const refreshParams = new URLSearchParams({

  grant_type: 'refresh_token',

  refresh_token: credentials.refreshToken,

  client_id: credentials.clientId,

  client_secret: credentials.clientSecret,

  resource: credentials.resource, // the crucial body param

});

const tokenResponse = await this.helpers.httpRequest({

  method: 'POST',

  url: credentials.accessTokenUrl,

  body: refreshParams.toString(),

  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },

});

// Update credentials cache

credentials.accessToken = tokenResponse.access_token;

credentials.refreshToken = tokenResponse.refresh_token;

credentials.expiresAt = Date.now() + (tokenResponse.expires_in * 1000);

await this.setCredentials('myCustomOAuth2Api', credentials);

}

// Attach access token to original request

request.headers.Authorization = Bearer ${credentials.accessToken};

return request;

}

Das ist im Grunde der Blueprint. Für Production würdest du Error Handling hinzufügen und Token Speicherung ordnungsgemäß durchführen (n8n’s Credentials-Objekt persistiert automatisch über die Datenbank).

Aber bevor du eine einzige Zeile Code schreibst — versuch zuerst den Query-Parameter-Trick. Hänge einfach ?resource=https://your-target an deine accessTokenUrl an. Wenn der Server das als Query-Parameter akzeptiert, bist du in 10 Sekunden fertig. Viele Custom OAuth Server tun das, weil sie beides parsen. Falls es streng Body-only ist, ist die Custom-Node-Route solid und langfristig wartbar.

Sag mir Bescheid, welchen Weg du am Ende gehst — ich helfe gerne beim Debuggen des preSend Hooks, wenn du steckenbleibst.

Hallo, für alle, die das überprüfen: hier ist ein Update dazu. Da der Auth-Server ein benutzerdefinierter war, konnten wir den Resource-Parameter aus einem vertrauenswürdigen Proxy in den Body des Token-Endpoints übertragen. Wir haben also keine Änderungen am Standard-Flow von n8n vorgenommen.

Hallo, danke für deine Antwort. Ich habe eine Update-Antwort zu dem Fall hinzugefügt, den ich erstellt habe. Wir konnten das über einen Trusted-Proxy-Ansatz lösen, anstatt einen benutzerdefinierten n8n-Credential-Flow zu verwenden. Ich glaube, dieser Ansatz ist sauberer und besser in Bezug auf Wartbarkeit.