diff --git a/src/servers/api/dav/sync-router.ts b/src/servers/api/dav/sync-router.ts index 3f7f6b8f..92c01561 100644 --- a/src/servers/api/dav/sync-router.ts +++ b/src/servers/api/dav/sync-router.ts @@ -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 }); });