docs(jellyfin,authentik): the LDAP admin filter is inert; the auth door lives in Users #143

Merged
dank merged 1 commits from docs/jellyfin-ldap-sso-laws into main 2026-11-04 12:27:01 -08:00
Owner

Why "the authentik login doesn't work in Jellyfin"

Nothing in the authentik → Jellyfin chain was broken. Two facts, both still true after this PR:

  • The Jellyfin admin account dank carries AuthenticationProviderId = …Users.DefaultAuthenticationProvider — a local password. alex is the only account on the LDAP provider.
  • The operator's authentik identity (akadmin) was not a member of jellyfin_users, so the plugin's search (memberOf=cn=jellyfin_users,…) found nothing and Jellyfin silently fell back to the local provider. Same UI message as a genuinely dead directory, opposite fix.

Live action taken (directory data, not repo state): akadmin added to jellyfin_users. Proven through the plugin's own door — the bind plus its exact filter now return cn=akadmin,ou=users,… with uid/cn/mail/displayName. First login as akadmin auto-creates a Jellyfin account (all folders, non-admin); dank remains the local break-glass admin.

What the docs got wrong

  • jellyfin_admins is inert. With EnableAdminFilterMemberUid=false the plugin grants admin only when an entry returned by the admin-filter search has the bound user's own DN (LDAP-Auth/LDAPAuthenticationProviderPlugin.cs). Our base ou=groups + (cn=jellyfin_admins) returns the group entry — measured: it answers cn=jellyfin_admins,ou=groups,… for every login, so no LDAP user can ever be admin. Working shape recorded: base dc=franksdank,dc=cloud + (memberOf=cn=jellyfin_admins,ou=groups,…). A malformed filter is not inert — it throws on every login, so the knob is all-or-nothing.
  • The "token intent" argument was moot. LDAP-Auth sends the service account's password (LdapBindPassword); it has no token field. Bind DN, the service account's usable password, and the search_full_directory path are now verified by a live bind from Dankserver instead of being marked unverified.
  • The LDAP outpost journal is blind to binds — only the 5-minute config cycle is ever logged. The instruments are ldapsearch with the plugin's own credentials/filter and the authentik event registry (21-day census: zero login_failed from Dankserver, i.e. no credential was ever submitted).
  • ak shell traps recorded: Event is keyed by created/action (no timestamp, no type), Outpost has no apps, and piping ak shell inside a heredoc binds the heredoc to grep so the shell gets no stdin and prints nothing.
  • Authentik.md stopped restating the LDAP outpost digest — the Image= line of the unit file is its only owner; the number cited there had gone stale twice.

Checks

./tests/run.sh → ALL TESTS PASSED (841). Docs-only change, so no deploy and no e2e gate.

Not done here

svc-jellyfin-ldap's bind password was printed into a session transcript during diagnosis (my redaction sed named the wrong XML element). Frank declined rotation for now; the credential is LAN-scoped and the transport is plain LDAP:389 by documented choice. Rotation is host-side: new password in authentik, rewrite LdapBindPassword in LDAP-Auth.xml, restart jellyfin.


cwd: /home/blank/.omp/wt/wt-20261104-121037-6d1fc1f · session: 01a1a833-a2e6-76b6-8a67-d54ca993dcca

## Why "the authentik login doesn't work in Jellyfin" Nothing in the authentik → Jellyfin chain was broken. Two facts, both still true after this PR: - The Jellyfin admin account `dank` carries `AuthenticationProviderId = …Users.DefaultAuthenticationProvider` — a **local** password. `alex` is the only account on the LDAP provider. - The operator's authentik identity (`akadmin`) was not a member of `jellyfin_users`, so the plugin's search (`memberOf=cn=jellyfin_users,…`) found nothing and Jellyfin silently fell back to the local provider. Same UI message as a genuinely dead directory, opposite fix. **Live action taken (directory data, not repo state):** `akadmin` added to `jellyfin_users`. Proven through the plugin's own door — the bind plus its exact filter now return `cn=akadmin,ou=users,…` with `uid/cn/mail/displayName`. First login as `akadmin` auto-creates a Jellyfin account (all folders, non-admin); `dank` remains the local break-glass admin. ## What the docs got wrong - **`jellyfin_admins` is inert.** With `EnableAdminFilterMemberUid=false` the plugin grants admin only when an entry returned by the admin-filter search has the **bound user's own DN** (`LDAP-Auth/LDAPAuthenticationProviderPlugin.cs`). Our base `ou=groups` + `(cn=jellyfin_admins)` returns the group entry — measured: it answers `cn=jellyfin_admins,ou=groups,…` for every login, so no LDAP user can ever be admin. Working shape recorded: base `dc=franksdank,dc=cloud` + `(memberOf=cn=jellyfin_admins,ou=groups,…)`. A *malformed* filter is not inert — it throws on every login, so the knob is all-or-nothing. - **The "token intent" argument was moot.** LDAP-Auth sends the service account's **password** (`LdapBindPassword`); it has no token field. Bind DN, the service account's usable password, and the `search_full_directory` path are now verified by a live bind from Dankserver instead of being marked unverified. - **The LDAP outpost journal is blind to binds** — only the 5-minute config cycle is ever logged. The instruments are `ldapsearch` with the plugin's own credentials/filter and the authentik event registry (21-day census: zero `login_failed` from Dankserver, i.e. no credential was ever submitted). - **`ak shell` traps recorded:** `Event` is keyed by `created`/`action` (no `timestamp`, no `type`), `Outpost` has no `apps`, and piping `ak shell` inside a heredoc binds the heredoc to `grep` so the shell gets no stdin and prints nothing. - **`Authentik.md` stopped restating the LDAP outpost digest** — the `Image=` line of the unit file is its only owner; the number cited there had gone stale twice. ## Checks `./tests/run.sh` → **ALL TESTS PASSED** (841). Docs-only change, so no deploy and no e2e gate. ## Not done here `svc-jellyfin-ldap`'s bind password was printed into a session transcript during diagnosis (my redaction `sed` named the wrong XML element). Frank declined rotation for now; the credential is LAN-scoped and the transport is plain LDAP:389 by documented choice. Rotation is host-side: new password in authentik, rewrite `LdapBindPassword` in `LDAP-Auth.xml`, restart jellyfin. --- cwd: /home/blank/.omp/wt/wt-20261104-121037-6d1fc1f · session: 01a1a833-a2e6-76b6-8a67-d54ca993dcca
dank added 1 commit 2026-11-04 12:26:29 -08:00
Diagnosed 'authentik login does not work in Jellyfin' 2026-11-04: nothing was
broken. The Jellyfin admin account 'dank' is a LOCAL-provider account, and the
operator's authentik identity (akadmin) was not in jellyfin_users, so the plugin
search found nothing and Jellyfin fell back to local auth - same UI message,
opposite fix. akadmin is now in jellyfin_users (directory data, DB-owned).

- Jellyfin: the admin-filter law (DN match, so a group-OU base grants nothing;
  memberOf shape is the working one), the AuthenticationProviderId diagnostic,
  re-verified LDAP path (bind + memberOf search from Dankserver).
- Authentik Outpost Reference: settle the token-intent argument (the plugin
  sends a password, never a token), verify the bind DN live, and record the
  instruments/traps: the outpost journal never shows a bind, Event is keyed by
  created/action, Outpost has no apps, and piping ak shell steals its heredoc.
- Authentik: stop restating the LDAP outpost digest - the unit file owns it.
dank merged commit 9961ed62c0 into main 2026-11-04 12:27:01 -08:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dank/serverAppConfigs#143