neuer Ansatz ...
This commit is contained in:
parent
03e0516c08
commit
31f03b0d7c
|
|
@ -1,4 +1,13 @@
|
|||
// qbo_helper.js - MIT OAUTH FLOW & VERBESSERTEM TOKEN HANDLING
|
||||
// qbo_helper.js - DEFINITIVER FIX
|
||||
//
|
||||
// Kernproblem: client.refresh() ruft intern validateToken() auf,
|
||||
// das das Token-Objekt prüft und "invalid" wirft wenn das Format
|
||||
// nicht stimmt. Das passiert LOKAL, nicht bei Intuit.
|
||||
//
|
||||
// Lösung: refreshUsingToken(refreshTokenString) verwenden.
|
||||
// Diese Methode akzeptiert den RT direkt als String und umgeht
|
||||
// die validateToken()-Prüfung komplett.
|
||||
|
||||
require('dotenv').config();
|
||||
const OAuthClient = require('intuit-oauth');
|
||||
const fs = require('fs');
|
||||
|
|
@ -35,10 +44,6 @@ const getOAuthClient = () => {
|
|||
oauthClient.setToken(savedToken);
|
||||
console.log("✅ Gespeicherter Token aus qbo_token.json geladen.");
|
||||
} else {
|
||||
// WICHTIG: intuit-oauth braucht ein VOLLSTÄNDIGES Token-Objekt!
|
||||
// Nur access_token + refresh_token reicht NICHT — die Library
|
||||
// prüft intern auf token_type, expires_in, createdAt etc.
|
||||
// und wirft "The Refresh token is invalid" wenn die fehlen.
|
||||
const envToken = {
|
||||
token_type: 'bearer',
|
||||
access_token: process.env.QBO_ACCESS_TOKEN || '',
|
||||
|
|
@ -52,25 +57,39 @@ const getOAuthClient = () => {
|
|||
oauthClient.setToken(envToken);
|
||||
console.log("ℹ️ Token aus .env geladen (Fallback).");
|
||||
} else {
|
||||
console.warn("⚠️ Kein gültiger Token vorhanden. Bitte unter Settings → QBO autorisieren.");
|
||||
console.warn("⚠️ Kein gültiger Token vorhanden.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return oauthClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setzt den oauthClient zurück, damit beim nächsten getOAuthClient()
|
||||
* der Token frisch aus der Datei geladen wird.
|
||||
*/
|
||||
function resetOAuthClient() {
|
||||
oauthClient = null;
|
||||
}
|
||||
|
||||
function saveTokens() {
|
||||
try {
|
||||
const token = getOAuthClient().getToken();
|
||||
fs.writeFileSync(tokenFile, JSON.stringify(token, null, 2));
|
||||
const client = getOAuthClient();
|
||||
const token = client.getToken();
|
||||
|
||||
// Debug: Was genau bekommen wir vom Client?
|
||||
console.log("💾 Speichere Token... refresh_token vorhanden:", !!token.refresh_token,
|
||||
"| access_token Länge:", (token.access_token || '').length,
|
||||
"| realmId:", token.realmId || 'FEHLT');
|
||||
|
||||
// Sicherstellen dass alle Pflichtfelder vorhanden sind
|
||||
const tokenToSave = {
|
||||
token_type: token.token_type || 'bearer',
|
||||
access_token: token.access_token,
|
||||
refresh_token: token.refresh_token,
|
||||
expires_in: token.expires_in || 3600,
|
||||
x_refresh_token_expires_in: token.x_refresh_token_expires_in || 8726400,
|
||||
realmId: token.realmId || process.env.QBO_REALM_ID,
|
||||
createdAt: token.createdAt || new Date().toISOString()
|
||||
};
|
||||
|
||||
fs.writeFileSync(tokenFile, JSON.stringify(tokenToSave, null, 2));
|
||||
console.log("💾 Tokens erfolgreich in qbo_token.json gespeichert.");
|
||||
} catch (e) {
|
||||
console.error("❌ Fehler beim Speichern der Tokens:", e.message);
|
||||
|
|
@ -80,27 +99,39 @@ function saveTokens() {
|
|||
async function makeQboApiCall(requestOptions) {
|
||||
const client = getOAuthClient();
|
||||
|
||||
// Prüfen ob überhaupt ein Refresh Token vorhanden ist
|
||||
const currentToken = client.getToken();
|
||||
if (!currentToken || !currentToken.refresh_token) {
|
||||
throw new Error("Kein gültiger QBO Token vorhanden. Bitte unter Settings → 'Authorize QBO' klicken.");
|
||||
throw new Error("Kein gültiger QBO Token vorhanden. Bitte Token erneuern.");
|
||||
}
|
||||
|
||||
const doRefresh = async () => {
|
||||
console.log("🔄 QBO Token Refresh wird ausgeführt...");
|
||||
|
||||
// Den Refresh Token als String extrahieren
|
||||
const refreshTokenStr = currentToken.refresh_token;
|
||||
console.log("🔑 Refresh Token (erste 15 Zeichen):", refreshTokenStr.substring(0, 15) + "...");
|
||||
|
||||
try {
|
||||
const authResponse = await client.refresh();
|
||||
console.log("✅ Token erfolgreich erneuert.");
|
||||
saveTokens();
|
||||
// KRITISCHER FIX: refreshUsingToken() statt refresh() verwenden!
|
||||
//
|
||||
// refresh() ruft intern validateToken() auf, das bei unvollständigem
|
||||
// Token-Objekt "The Refresh token is invalid" wirft — OHNE jemals
|
||||
// Intuit zu kontaktieren.
|
||||
//
|
||||
// refreshUsingToken() akzeptiert den RT als String und umgeht das.
|
||||
const authResponse = await client.refreshUsingToken(refreshTokenStr);
|
||||
console.log("✅ Token erfolgreich erneuert via refreshUsingToken().");
|
||||
saveTokens();
|
||||
return authResponse;
|
||||
} catch (e) {
|
||||
const errMsg = e.originalMessage || e.message || String(e);
|
||||
console.error("❌ Refresh fehlgeschlagen:", errMsg);
|
||||
if (e.intuit_tid) console.error(" intuit_tid:", e.intuit_tid);
|
||||
|
||||
// Wenn der Refresh Token komplett ungültig ist → klare Meldung
|
||||
if (errMsg.includes('invalid') || errMsg.includes('Authorize again')) {
|
||||
if (errMsg.includes('invalid_grant')) {
|
||||
throw new Error(
|
||||
"Der Refresh Token ist abgelaufen. Bitte unter Settings → 'Authorize QBO' neu autorisieren."
|
||||
"Der Refresh Token ist bei Intuit ungültig (invalid_grant). " +
|
||||
"Bitte im Playground einen neuen Token holen und set_qbo_token.js ausführen."
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
|
|
@ -110,17 +141,14 @@ async function makeQboApiCall(requestOptions) {
|
|||
try {
|
||||
const response = await client.makeApiCall(requestOptions);
|
||||
|
||||
// Prüfen, ob die Antwort JSON ist (manche Auth-Fehler sind HTML/Text)
|
||||
const data = response.getJson ? response.getJson() : response.json;
|
||||
|
||||
if (data.fault && data.fault.error) {
|
||||
const errorCode = data.fault.error[0].code;
|
||||
|
||||
// 3200 (Auth Failed), 3202, 3100 → Refresh versuchen
|
||||
if (errorCode === '3200' || errorCode === '3202' || errorCode === '3100') {
|
||||
console.log(`⚠️ QBO meldet Token-Fehler (${errorCode}). Versuche Refresh und Retry...`);
|
||||
await doRefresh();
|
||||
// Retry mit neuem Token
|
||||
return await client.makeApiCall(requestOptions);
|
||||
}
|
||||
throw new Error(`QBO API Error ${errorCode}: ${data.fault.error[0].message}`);
|
||||
|
|
@ -130,7 +158,6 @@ async function makeQboApiCall(requestOptions) {
|
|||
return response;
|
||||
|
||||
} catch (e) {
|
||||
// HTTP 401 Unauthorized fangen (falls die Lib wirft, statt data.fault zurückzugeben)
|
||||
const isAuthError =
|
||||
e.response?.status === 401 ||
|
||||
(e.authResponse && e.authResponse.response && e.authResponse.response.status === 401) ||
|
||||
|
|
|
|||
Loading…
Reference in New Issue