Add detailed debug logging for the login flow

The login flow was near-silent: three info lines with no way to see why a
session was considered expired or what happened to the cookies. Debugging
a failing login meant guessing.

Every login step now logs on debug level with a `phase` field: the login
page fetch, the extracted nonce, the POST to /login/api/login.php, the
session check, and any request retried after a re-login. Each of those
requests is bracketed by a cookie snapshot taken before it is sent and one
taken after, plus a delta of added/changed/removed cookies. The "before"
snapshot is logged prior to sending, so it survives a hanging request.

isLoginRequiredResponse() is split so the criterion that matched
(status-401, login-markup, ...) can be reported instead of just a boolean.

Secrets stay out of the log: the password is replaced by ***, and cookie
values - including those in the raw Set-Cookie header - are truncated to
their first four characters plus length. That still shows whether a
session id changed without putting the session itself in the log.

Detail logging is strictly debug-level via logLoginDetail(); the existing
logDebug() falls back to info, which would flood a plain info logger. When
no debug logger is attached, no snapshot is taken and nothing is
serialized.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-07 20:05:02 +02:00
co-authored by Claude Opus 5
parent b844b772b8
commit 4cd0da4a8a
7 changed files with 674 additions and 22 deletions
+31
View File
@@ -50,6 +50,11 @@ export declare class AgrarmonitorConnector implements AgrarmonitorConnectorResul
private isSessionValid;
private loginRequestConfig;
private isLoginRequiredResponse;
/**
* Liefert das Kriterium, an dem eine abgelaufene Session erkannt wurde,
* oder null fuer eine gueltige Session.
*/
private loginRequiredReason;
private retryAfterLogin;
private createDateienLivesearchParams;
private getEingangsrechnungEditMeta;
@@ -80,5 +85,31 @@ export declare class AgrarmonitorConnector implements AgrarmonitorConnectorResul
private isLoginPageText;
private extractNonce;
private maskNonce;
private get debugLogging();
/**
* Detail-Logging des Login-Flows. Anders als logDebug faellt das bewusst
* NICHT auf info zurueck - ohne debug-Logger bleiben die Details still.
*/
private logLoginDetail;
private maskValue;
/**
* Liest die Cookies des Jars fuer eine URL aus. Liefert eine leere Liste,
* solange kein debug-Logger haengt - dann wird gar nicht erst serialisiert.
*/
private snapshotCookies;
/**
* Maskiert die Werte im rohen Set-Cookie-Header - sonst stuende die neue
* Session-ID im Klartext im Log, obwohl der Jar-Snapshot sie maskiert.
*/
private formatSetCookieHeader;
/** Wie snapshotCookies, aber mit maskierten Werten - nur das geht ins Log. */
private formatCookies;
private diffCookies;
/**
* Klammert einen Request mit je einem Cookie-Snapshot davor und danach.
* Der Snapshot davor wird vor dem Absenden geloggt, damit er auch dann
* im Log steht, wenn der Request haengt oder wirft.
*/
private loggedRequest;
}
//# sourceMappingURL=AgrarmonitorConnector.d.ts.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"AgrarmonitorConnector.d.ts","sourceRoot":"","sources":["../src/AgrarmonitorConnector.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"AgrarmonitorConnector.d.ts","sourceRoot":"","sources":["../src/AgrarmonitorConnector.ts"],"names":[],"mappings":"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"}
+185 -9
View File
@@ -486,6 +486,14 @@ class AgrarmonitorConnector {
throw new Error('Agrarmonitor-Credentials nicht konfiguriert');
}
this.logger?.info?.('Fuehre Agrarmonitor-Login durch');
this.logLoginDetail('Login gestartet', {
phase: 'login-start',
baseUrl: this.baseUrl,
username: this.options.username,
loginStrategy: this.loginStrategy,
autoRetry: this.autoRetry,
cookies: this.formatCookies(this.snapshotCookies()),
});
if (this.loginStrategy === 'auto') {
this.logger?.warn?.('loginStrategy "auto" ist veraltet; verwende Redirect-Login');
}
@@ -495,14 +503,27 @@ class AgrarmonitorConnector {
}
await this.options.cookieStore.save(this.cookieJar);
this.logger?.info?.('Agrarmonitor-Login erfolgreich');
this.logLoginDetail('Login abgeschlossen', {
phase: 'login-done',
cookies: this.formatCookies(this.snapshotCookies()),
});
}
async performRedirectLogin() {
const loginPageResponse = await this.http.get('/', this.loginRequestConfig());
const loginPageResponse = await this.loggedRequest('Loginseite', 'GET', '/', () => this.http.get('/', this.loginRequestConfig()));
const loginPageText = typeof loginPageResponse.data === 'string' ? loginPageResponse.data : '';
if (!this.isLoginPageText(loginPageText)) {
this.logLoginDetail('Keine Loginseite erhalten, Session ist bereits gueltig', {
phase: 'login-skip',
url: '/',
});
return;
}
const nonce = this.extractNonce(loginPageText, 'input[name="nonce"]');
this.logLoginDetail('Nonce aus Loginseite gelesen', {
phase: 'nonce',
url: '/',
nonce: this.maskNonce(nonce),
});
const loginData = {
username: this.options.username,
passwort: this.options.password,
@@ -516,7 +537,10 @@ class AgrarmonitorConnector {
},
_agrarmonitorLoginRequest: true,
};
const response = await this.http.post('/login/api/login.php', loginData, loginPostConfig);
const response = await this.loggedRequest('Login-POST', 'POST', '/login/api/login.php', () => this.http.post('/login/api/login.php', loginData, loginPostConfig), {
contentType: 'application/json',
body: { ...loginData, passwort: '***', nonce: this.maskNonce(nonce) },
});
const responseText = typeof response.data === 'string' ? response.data : '';
if (this.isLoginPageText(responseText)) {
throw new Error('Agrarmonitor-Redirect-Login fehlgeschlagen');
@@ -525,9 +549,25 @@ class AgrarmonitorConnector {
async isSessionValid(options = {}) {
try {
const response = await this.http.get('/', options.skipAutoRetry ? this.loginRequestConfig() : undefined);
return !this.isLoginRequiredResponse(response);
const grund = this.loginRequiredReason(response);
this.logLoginDetail(`Session-Pruefung: ${grund ? 'ungueltig' : 'gueltig'}`, {
phase: 'session-check',
url: '/',
status: response.status,
gueltig: grund === null,
grund,
cookies: this.formatCookies(this.snapshotCookies()),
});
return grund === null;
}
catch {
catch (error) {
this.logLoginDetail('Session-Pruefung fehlgeschlagen', {
phase: 'session-check',
url: '/',
gueltig: false,
grund: 'request-fehler',
fehler: error instanceof Error ? error.message : String(error),
});
return false;
}
}
@@ -537,20 +577,38 @@ class AgrarmonitorConnector {
};
}
isLoginRequiredResponse(response) {
const responseUrl = this.getResponseUrl(response);
return this.loginRequiredReason(response) !== null;
}
/**
* Liefert das Kriterium, an dem eine abgelaufene Session erkannt wurde,
* oder null fuer eine gueltige Session.
*/
loginRequiredReason(response) {
const responseText = typeof response.data === 'string' ? response.data : '';
return (response.status === 401 ||
response.status === 403 ||
this.isLoginPageText(responseText));
if (response.status === 401 || response.status === 403) {
return `status-${response.status}`;
}
if (this.isLoginPageText(responseText)) {
return 'login-markup';
}
return null;
}
async retryAfterLogin(config) {
if (config._agrarmonitorRetry) {
throw new Error('Agrarmonitor-Request nach erneutem Login weiterhin nicht autorisiert');
}
config._agrarmonitorRetry = true;
const method = (config.method ?? 'get').toUpperCase();
const url = config.url ?? '/';
this.logger?.info?.('Agrarmonitor-Session abgelaufen, erneuter Login wird ausgefuehrt');
this.logLoginDetail(`Session abgelaufen, wiederhole ${method} ${url} nach erneutem Login`, {
phase: 'session-expired',
method,
url,
cookies: this.formatCookies(this.snapshotCookies()),
});
await this.login();
return this.http.request(config);
return this.loggedRequest('Wiederholung nach Login', method, url, () => this.http.request(config), {}, 'retry');
}
createDateienLivesearchParams(suchstring) {
return {
@@ -731,6 +789,124 @@ class AgrarmonitorConnector {
maskNonce(nonce) {
return nonce.length <= 10 ? nonce : `${nonce.slice(0, 10)}...`;
}
get debugLogging() {
return typeof this.logger?.debug === 'function';
}
/**
* Detail-Logging des Login-Flows. Anders als logDebug faellt das bewusst
* NICHT auf info zurueck - ohne debug-Logger bleiben die Details still.
*/
logLoginDetail(message, meta) {
this.logger?.debug?.(message, meta);
}
maskValue(value) {
if (!value) {
return '(leer)';
}
return value.length <= 8 ? `\u2026(len=${value.length})` : `${value.slice(0, 4)}\u2026(len=${value.length})`;
}
/**
* Liest die Cookies des Jars fuer eine URL aus. Liefert eine leere Liste,
* solange kein debug-Logger haengt - dann wird gar nicht erst serialisiert.
*/
snapshotCookies(url = this.baseUrl) {
if (!this.debugLogging) {
return [];
}
try {
return this.cookieJar.getCookiesSync(url).map(cookie => ({
name: cookie.key,
value: cookie.value,
domain: cookie.domain ?? null,
path: cookie.path ?? null,
expires: cookie.expires instanceof Date ? cookie.expires.toISOString() : 'Session',
httpOnly: Boolean(cookie.httpOnly),
secure: Boolean(cookie.secure),
}));
}
catch {
return [];
}
}
/**
* Maskiert die Werte im rohen Set-Cookie-Header - sonst stuende die neue
* Session-ID im Klartext im Log, obwohl der Jar-Snapshot sie maskiert.
*/
formatSetCookieHeader(header) {
if (!Array.isArray(header)) {
return null;
}
return header.map(entry => {
const [pair, ...attributes] = String(entry).split(';');
const separator = pair.indexOf('=');
if (separator === -1) {
return [pair, ...attributes].join(';');
}
const name = pair.slice(0, separator);
const value = pair.slice(separator + 1);
return [`${name}=${this.maskValue(value)}`, ...attributes].join(';');
});
}
/** Wie snapshotCookies, aber mit maskierten Werten - nur das geht ins Log. */
formatCookies(cookies) {
return cookies.map(cookie => ({ ...cookie, value: this.maskValue(cookie.value) }));
}
diffCookies(before, after) {
const beforeByName = new Map(before.map(cookie => [cookie.name, cookie.value]));
const afterByName = new Map(after.map(cookie => [cookie.name, cookie.value]));
return {
added: [...afterByName.keys()].filter(name => !beforeByName.has(name)),
changed: [...afterByName.entries()]
.filter(([name, value]) => beforeByName.has(name) && beforeByName.get(name) !== value)
.map(([name]) => name),
removed: [...beforeByName.keys()].filter(name => !afterByName.has(name)),
};
}
/**
* Klammert einen Request mit je einem Cookie-Snapshot davor und danach.
* Der Snapshot davor wird vor dem Absenden geloggt, damit er auch dann
* im Log steht, wenn der Request haengt oder wirft.
*/
async loggedRequest(label, method, url, exec, details = {}, phase = 'request') {
const before = this.snapshotCookies();
this.logLoginDetail(`${label}: ${method} ${url}`, {
phase,
method,
url,
cookies: this.formatCookies(before),
...details,
});
try {
const response = await exec();
const after = this.snapshotCookies();
const responseText = typeof response.data === 'string' ? response.data : '';
this.logLoginDetail(`${label}: ${method} ${url} -> ${response.status}`, {
phase: 'response',
method,
url,
status: response.status,
responseUrl: this.getResponseUrl(response) || null,
contentType: this.getHeader(response, 'content-type'),
setCookie: this.formatSetCookieHeader(response.headers['set-cookie']),
istLoginSeite: this.isLoginPageText(responseText),
cookies: this.formatCookies(after),
cookieChanges: this.diffCookies(before, after),
});
return response;
}
catch (error) {
const after = this.snapshotCookies();
this.logLoginDetail(`${label}: ${method} ${url} -> Fehler`, {
phase: 'error',
method,
url,
fehler: error instanceof Error ? error.message : String(error),
cookies: this.formatCookies(after),
cookieChanges: this.diffCookies(before, after),
});
throw error;
}
}
}
exports.AgrarmonitorConnector = AgrarmonitorConnector;
//# sourceMappingURL=AgrarmonitorConnector.js.map
+1 -1
View File
File diff suppressed because one or more lines are too long