log every dav request, and every rejected credential

a DAV client tells you almost nothing when it fails — iOS reports every setup
failure as "Cannot connect using SSL" regardless of cause — so the only way to
know whether a phone ever asked for an address book is to record that it did.

one line per proxied request with the client's own user-agent, and a warning
when a credential is present but wrong. a missing credential is the normal
opening move and stays unlogged; a wrong one is indistinguishable from it at
the client end, where both just say "password incorrect".

no body is ever logged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 11:19:50 +00:00
co-authored by Claude Opus 5
parent c4fecc201b
commit 75b3484636
+20 -2
View File
@@ -79,11 +79,20 @@ davSyncRouter.all('/*', async (ctx) => {
const creds = parseBasic(ctx.req.header('authorization'));
if (!creds) return unauthorized();
// A credential-less request is the normal opening move — the client is asking to be challenged — so it
// is not worth a line. A credential that is present and wrong is the interesting case, and it is
// indistinguishable from the normal one at the client end: both just say "password incorrect".
const user = await getUserByEmail(creds.username);
if (!user) return unauthorized();
if (!user) {
console.warn(`[dav] auth failed: no such user ${creds.username}`);
return unauthorized();
}
const userId = await verifyDavAppPassword(user.id, creds.password);
if (!userId) return unauthorized();
if (!userId) {
console.warn(`[dav] auth failed: bad app password for ${creds.username}`);
return unauthorized();
}
const base = getCaldavUrl();
if (!base) return new Response('caldav sidecar not available', { status: 503 });
@@ -121,5 +130,14 @@ davSyncRouter.all('/*', async (ctx) => {
const value = upstream.headers.get(name);
if (value) out.set(name, value);
}
// One line per request, with the client's own name for itself. A DAV client tells you almost nothing
// when it fails — iOS in particular reports every setup failure as "Cannot connect using SSL" — so the
// only way to know whether a phone ever asked for an address book is to record that it did. Cheap:
// a sync is a handful of requests, and a body is never logged.
console.log(
`[dav] ${method} ${url.pathname} -> ${upstream.status} (user=${userId}, ua=${(ctx.req.header('user-agent') ?? '-').slice(0, 60)})`,
);
return new Response(upstream.body, { status: upstream.status, headers: out });
});