From 5be66df2bb97d434c36ea9a65b51232c968a1f69 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 3 Nov 2026 02:28:11 -0800 Subject: [PATCH] docs(audiobookshelf): /api/login is not a route A wrong path was mistaken for a dead break-glass credential. Local login is POST /login; /api/* is behind the bearer guard, which answers a bare 401 and logs nothing, so a bad path looks exactly like a bad password. The recorded value bcrypt-matches users.pash, so local really is the door when authentik is down, and the rate-limit line was protecting a path that cannot carry a login. Also records what auto-registration actually grants, measured from the state DB because GET /api/users returns the limited shape: every SSO signer arrives with accessAllLibraries and download already on, and carries no local password. --- .../Dankserver/Audiobookshelf.md | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/serverAppConfigs-docs/Dankserver/Audiobookshelf.md b/serverAppConfigs-docs/Dankserver/Audiobookshelf.md index 312a0c1..8dd8f5c 100644 --- a/serverAppConfigs-docs/Dankserver/Audiobookshelf.md +++ b/serverAppConfigs-docs/Dankserver/Audiobookshelf.md @@ -61,6 +61,9 @@ Dankserver. Change it in **Settings → Users → frank → Password**; the secr then holds a stale value and only matters again if the `config` bind is ever destroyed (a fresh DB makes the script create a new owner from it). +That value is the live password, not a leftover: it bcrypt-matches `users.pash` in the state +DB (2026-11-03), so `local` does open the door when authentik is down. + A full scan is slow and runs in the background; watch the `items:` total climb, never `/healthcheck`. It also reads files that are genuinely broken and logs `[AudioFileScanner] Invalid data found when processing input` per file — that is @@ -90,6 +93,15 @@ inside the user object. A client that reads `response.token` gets the empty stri `Authorization: Bearer ` with nothing after it, and every `/api/*` call then answers `401` as though the password were wrong. +**`POST /api/login` is not a route.** Local login is `POST /login`, and the `/api` +bearer guard answers anything else with a bare `401 Unauthorized` while logging **nothing** — +no `[LocalAuth]` line — so a wrong path is indistinguishable from a wrong password unless the +journal is read too. Same family as the absent `/setup` route named in +`scripts/audiobookshelf-init.sh`. To settle a suspected stale credential without signing in: +bcrypt-compare the recorded value against `users.pash` in +`/home/dank/Containers/audiobookshelf/config/absdatabase.sqlite`, opened read-only +(`file:?mode=ro`). + ## Listeners (accounts) The door is public now, so the account is the only gate between a listener and the library. @@ -103,7 +115,14 @@ Two ways in, both per listener: listener, because the claim grants that role to every signer. To shut a listener off, disable or delete their user in Audiobookshelf — no IdP group membership can withhold it. -Nothing rate-limits `/audiobookshelf/api/login` — this Caddy build carries no `rate_limit` +What auto-registration hands out (read off the state DB, 2026-11-03): `accessAllLibraries: +true` and `download: true` — every household signer lands on the **whole** library with +offline downloads already on. Narrow it per account in **Settings → Users → **. The DB +is the census when no admin token is at hand; `GET /api/users` returns the *limited* shape +(no `active`, no `librariesAccessible`), so a missing key there is not a denial, and an +account created by SSO has `pash` NULL with its IdP link at `extraData.authOpenIDSub`. + +Nothing rate-limits `POST /audiobookshelf/login` — this Caddy build carries no `rate_limit` module and ABS 2.37.2 ships no lockout — so the form is a guessing target exactly like `jelly.`, `plex.` and the *arr logins already are. The mitigation is credential hygiene: long unique passwords per listener, never a reuse of the root password. -- 2.54.0