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.
## 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
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 main2026-11-04 12:27:01 -08:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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:
dankcarriesAuthenticationProviderId = …Users.DefaultAuthenticationProvider— a local password.alexis the only account on the LDAP provider.akadmin) was not a member ofjellyfin_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):
akadminadded tojellyfin_users. Proven through the plugin's own door — the bind plus its exact filter now returncn=akadmin,ou=users,…withuid/cn/mail/displayName. First login asakadminauto-creates a Jellyfin account (all folders, non-admin);dankremains the local break-glass admin.What the docs got wrong
jellyfin_adminsis inert. WithEnableAdminFilterMemberUid=falsethe 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 baseou=groups+(cn=jellyfin_admins)returns the group entry — measured: it answerscn=jellyfin_admins,ou=groups,…for every login, so no LDAP user can ever be admin. Working shape recorded: basedc=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.LdapBindPassword); it has no token field. Bind DN, the service account's usable password, and thesearch_full_directorypath are now verified by a live bind from Dankserver instead of being marked unverified.ldapsearchwith the plugin's own credentials/filter and the authentik event registry (21-day census: zerologin_failedfrom Dankserver, i.e. no credential was ever submitted).ak shelltraps recorded:Eventis keyed bycreated/action(notimestamp, notype),Outposthas noapps, and pipingak shellinside a heredoc binds the heredoc togrepso the shell gets no stdin and prints nothing.Authentik.mdstopped restating the LDAP outpost digest — theImage=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 redactionsednamed 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, rewriteLdapBindPasswordinLDAP-Auth.xml, restart jellyfin.cwd: /home/blank/.omp/wt/wt-20261104-121037-6d1fc1f · session: 01a1a833-a2e6-76b6-8a67-d54ca993dcca