docs(audiobookshelf): the two IdP laws the dead login taught, and the gate
Records that an ORM-created provider has no grant types and that the claim mapping is not the whole property_mappings list, with the failure signatures to recognise; names qa-e2e-admin as the admin-group fixture and the new Playwright spec as the live verification.
This commit is contained in:
1 parent
d82b8efdd7
commit
54ec72da17
1 file changed
+27
-2
@@ -97,12 +97,15 @@ the local form, and the phone app offers SSO on its server-address screen. The l
|
||||
| Client secret | `ABS_OIDC_CLIENT_SECRET` in `~/.config/secrets/audiobookshelf` on Dankserver (mode 600; never in the repo, a URL or an argv) |
|
||||
| The ABS half: issuer, the four IdP URLs, claim, auto-register | `scripts/audiobookshelf-init.sh`, armed automatically when that secret exists in the credential file |
|
||||
| Role at first SSO sign-in | the `abs_groups` claim: `audiobookshelf_admins` → `admin`, `audiobookshelf_users` → `user`, in neither → denied (fail closed) |
|
||||
| Admin group members | `akadmin` (operator stand-in) and `qa-e2e-admin` — the second is the identity the deploy gate signs in as, so the script re-adds it on every run; drop it and the gate fails, not a phone |
|
||||
|
||||
The IdP saying yes is not the same as being allowed in: with `authOpenIDAutoRegister` on,
|
||||
a first sign-in creates the account, and the claim alone decides the type. A user in
|
||||
neither group is refused after a successful authentication — that is the mapping working,
|
||||
not a broken login.
|
||||
|
||||
Both halves — authentik's provider row and ABS's settings — are asserted end to end by the Playwright gate named in *Verification*: a sign-in as `qa-e2e-admin` that must land inside the app with site-admin rights.
|
||||
|
||||
## Laws
|
||||
|
||||
- **Speak the image's env names, not the README's.** The server reads `PORT`,
|
||||
@@ -128,6 +131,21 @@ not a broken login.
|
||||
provider's registered URI must equal `<origin><subfolder>/auth/openid/callback`, so each
|
||||
script derives the value from one variable of its own (`ABS_URL`, `ABS_BASE`) and
|
||||
`tests/audiobookshelf_init.bats` pins what reaches the wire.
|
||||
- **An ORM-created provider has no grant types.** authentik 2026.8 defaults `grant_types` to
|
||||
`[]`, and `/application/o/authorize/` then refuses the code flow with oauthlib's generic
|
||||
"The request is otherwise malformed" — the wire names nothing, the server log line that names
|
||||
it is `Invalid grant_type for provider`. Worse, the redirect-URI check runs *first*, so a
|
||||
provider carrying both faults shows only "Redirect URI Error" until the URI is fixed, and the
|
||||
second wall appears after the fix ships (measured 2026-11-02: the browser reached authentik,
|
||||
got `code`+`state`, and ABS still bounced to `/login?error=undefined`). The script sets
|
||||
`authorization_code` + `refresh_token`; a blueprint re-import reverts the row silently.
|
||||
- **The claim mapping is not the whole `property_mappings` list.** A provider holding only the
|
||||
custom mapping grants a token scoped shorter than the scopes ABS requests
|
||||
(`openid profile email abs_groups`): the code is issued and exchanged normally, and ABS's
|
||||
`/userinfo` call then dies `insufficient_scope (The request requires higher privileges than
|
||||
provided by the access token)` — a fault that reads like an ABS bug and is an IdP row. Attach
|
||||
the built-in OpenID `openid`/`profile`/`email` mappings beside the claim mapping, which is
|
||||
what every working RP on this IdP carries (measured 2026-11-02).
|
||||
- **The audio bind stays `:ro`.** ABS writes nothing that belongs beside the
|
||||
audio: its library, covers, progress and sidecar metadata all land in
|
||||
`/metadata` on the SSD. A writable bind would put stray writes on the spindle
|
||||
@@ -248,8 +266,15 @@ not a broken login.
|
||||
health check, the base path the shipped SPA needs), `tests/audiobookshelf_init.bats` (the
|
||||
bootstrapper against a stub of the API above: create-once, idempotent re-run, no secret in a
|
||||
URL, refusal by URL when the server does not answer, the deployer calling it only after the
|
||||
health gate, and the SSO arm sending the mounted base path as the redirect subfolder) and
|
||||
`tests/media_overlay.bats` (only [[Plex]] and [[Jellyfin]] may reference the metadata view).
|
||||
health gate, and the SSO arm sending the mounted base path as the redirect subfolder);
|
||||
`tests/media_overlay.bats` (only [[Plex]] and [[Jellyfin]] may reference the metadata view);
|
||||
and the live Playwright gate `tests/e2e/specs/23-audiobookshelf-sso.spec.ts`, run by
|
||||
`tests/e2e/run.sh` at the end of every `deploy.sh` — the `admin` project signs in as
|
||||
`qa-e2e-admin` against `http://192.168.0.235:8000/audiobookshelf` and asserts the two things
|
||||
no stub can see: every `.js` the app loads arrives as JavaScript (a bundle built for the wrong
|
||||
base path arrives as `text/html`, which boots nothing), and `/api/me` answers 200 with
|
||||
`type: "admin"` — true only while grant types, the OpenID scope mappings and the group claim
|
||||
all hold together.
|
||||
|
||||
Five book folders hold media no scanner can read, so they stay out of the library:
|
||||
three 2024 Nebula nominees in the `_unsorted` box (two `.m4b` with no `moov` atom,
|
||||
|
||||
Reference in new issue
Block a user