<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>authentik on Samirtech</title>
    <link>https://samirtech.org/tags/authentik/</link>
    <description>Recent content in authentik on Samirtech</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 25 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://samirtech.org/tags/authentik/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Authentik with Jellyfin and Navidrome: Two Different Paths to One Identity</title>
      <link>https://samirtech.org/posts/authentik-with-jellyfin-and-navidrome/</link>
      <pubDate>Fri, 25 Sep 2026 00:00:00 +0000</pubDate>
      
      <guid>https://samirtech.org/posts/authentik-with-jellyfin-and-navidrome/</guid>
      <description>Jellyfin and Navidrome can use the same Authentik directory, but they do not authenticate in the same way.
In my setup, Jellyfin uses LDAP. Navidrome&amp;rsquo;s web interface sits behind Authentik and Caddy, with the authenticated username passed in an HTTP header. Navidrome&amp;rsquo;s OpenSubsonic API remains separate so applications such as Symfonium and Feishin can use native credentials.
That distinction is the key to making the arrangement understandable and safe.
The three authentication paths Jellyfin -&amp;gt; LDAP plugin -&amp;gt; Authentik LDAP outpost Browser -&amp;gt; Caddy -&amp;gt; Authentik forward auth -&amp;gt; Navidrome OpenSubsonic client -&amp;gt; /rest/* -&amp;gt; Navidrome credentials Trying to force all three through one browser-oriented SSO flow causes problems.</description>
      <content>&lt;p&gt;Jellyfin and Navidrome can use the same Authentik directory, but they do not authenticate in the same way.&lt;/p&gt;
&lt;p&gt;In my setup, Jellyfin uses LDAP. Navidrome&amp;rsquo;s web interface sits behind Authentik and Caddy, with the authenticated username passed in an HTTP header. Navidrome&amp;rsquo;s OpenSubsonic API remains separate so applications such as Symfonium and Feishin can use native credentials.&lt;/p&gt;
&lt;p&gt;That distinction is the key to making the arrangement understandable and safe.&lt;/p&gt;
&lt;h2 id=&#34;the-three-authentication-paths&#34;&gt;The three authentication paths&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Jellyfin -&amp;gt; LDAP plugin -&amp;gt; Authentik LDAP outpost
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Browser -&amp;gt; Caddy -&amp;gt; Authentik forward auth -&amp;gt; Navidrome
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OpenSubsonic client -&amp;gt; /rest/* -&amp;gt; Navidrome credentials
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Trying to force all three through one browser-oriented SSO flow causes problems. Native media clients expect an API response, not an HTML login page or a redirect.&lt;/p&gt;
&lt;h2 id=&#34;jellyfin-ldap-needs-an-ldap-service&#34;&gt;Jellyfin: LDAP needs an LDAP service&lt;/h2&gt;
&lt;p&gt;Installing Jellyfin&amp;rsquo;s LDAP plugin is only the client half of the job. Authentik must already have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an LDAP provider;&lt;/li&gt;
&lt;li&gt;an LDAP outpost serving that provider;&lt;/li&gt;
&lt;li&gt;a bind account for Jellyfin;&lt;/li&gt;
&lt;li&gt;a user search base and attribute that match the directory; and&lt;/li&gt;
&lt;li&gt;an explicit rule for which users may log in.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The outpost must be reachable from Jellyfin&amp;rsquo;s own network namespace. A connection test from a laptop does not prove that a container can resolve or reach the same endpoint.&lt;/p&gt;
&lt;p&gt;The useful troubleshooting order is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;confirm that the LDAP outpost is healthy;&lt;/li&gt;
&lt;li&gt;test DNS and the LDAP port from Jellyfin&amp;rsquo;s network;&lt;/li&gt;
&lt;li&gt;verify TLS trust, if TLS is enabled;&lt;/li&gt;
&lt;li&gt;verify the bind DN and secret;&lt;/li&gt;
&lt;li&gt;check the user base, username attribute and filter; and&lt;/li&gt;
&lt;li&gt;inspect both the Jellyfin and outpost logs during one login attempt.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If every user fails, the transport, bind or search configuration is probably wrong. If valid users can authenticate but the wrong users are also admitted, the connection works and the scope is too broad.&lt;/p&gt;
&lt;h2 id=&#34;use-an-allow-list-not-a-guest-deny-list&#34;&gt;Use an allow-list, not a guest deny-list&lt;/h2&gt;
&lt;p&gt;I did not want every Authentik account to inherit Jellyfin access. In particular, guest identities needed to remain restricted.&lt;/p&gt;
&lt;p&gt;The safer model is an approved media group. The Authentik provider can expose only the intended users, while Jellyfin&amp;rsquo;s LDAP configuration also requires membership of that group. This is easier to audit than maintaining a growing list of exclusions.&lt;/p&gt;
&lt;p&gt;A generic user filter might resemble:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-ldap&#34; data-lang=&#34;ldap&#34;&gt;(&amp;amp;(objectClass=&amp;lt;USER_OBJECT_CLASS&amp;gt;)(&amp;lt;USERNAME_ATTRIBUTE&amp;gt;={0}))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A group restriction depends on the schema exposed by the installed Authentik version and the syntax expected by the Jellyfin plugin. Copying a filter from another directory without inspecting the actual attributes is unreliable.&lt;/p&gt;
&lt;p&gt;Test four cases before considering it finished:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an approved user with the correct password;&lt;/li&gt;
&lt;li&gt;a valid Authentik user outside the media group;&lt;/li&gt;
&lt;li&gt;a guest account; and&lt;/li&gt;
&lt;li&gt;an approved user with the wrong password.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Only the first should succeed.&lt;/p&gt;
&lt;p&gt;LDAP authentication also does not define every Jellyfin policy. Library access and administrator privileges still need to be reviewed on the resulting Jellyfin user.&lt;/p&gt;
&lt;h2 id=&#34;navidrome-forward-authentication-through-caddy&#34;&gt;Navidrome: forward authentication through Caddy&lt;/h2&gt;
&lt;p&gt;Navidrome can trust a username header supplied by an approved reverse proxy. In outline, its external-authentication settings look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ND_EXTAUTH_USERHEADER&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;X-Authentik-Username&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ND_EXTAUTH_TRUSTEDSOURCES&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;CADDY_SOURCE_ADDRESS&amp;gt;/32&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ND_EXTAUTH_AUTOUSERCREATION&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Check the names against the installed Navidrome release before applying them.&lt;/p&gt;
&lt;p&gt;Caddy asks Authentik to authorise the browser request and copies the returned identity headers:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-caddyfile&#34; data-lang=&#34;caddyfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;music.example.invalid {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;route&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;reverse_proxy&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;/outpost.goauthentik.io/*&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;AUTHENTIK_OUTPOST&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;@browser&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;/rest/*&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;/share/*&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;/outpost.goauthentik.io/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;forward_auth&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;@browser&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;AUTHENTIK_OUTPOST&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;uri&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;/outpost.goauthentik.io/auth/caddy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;copy_headers&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;X-Authentik-Username&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;X-Authentik-Groups&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;X-Authentik-Email&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;reverse_proxy&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;NAVIDROME_UPSTREAM&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is an architectural example, not a drop-in configuration. The recommended outpost route can change between Authentik versions.&lt;/p&gt;
&lt;p&gt;With automatic user creation enabled, the first successful browser login creates a corresponding Navidrome account. New accounts should still be checked to ensure they have not received administrator rights accidentally.&lt;/p&gt;
&lt;h2 id=&#34;the-trusted-source-trap&#34;&gt;The trusted-source trap&lt;/h2&gt;
&lt;p&gt;The username header is not proof of identity by itself. Any client can try to send &lt;code&gt;X-Authentik-Username&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It becomes trustworthy only when:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Caddy ignores or replaces any client-supplied identity header;&lt;/li&gt;
&lt;li&gt;Authentik validates the request;&lt;/li&gt;
&lt;li&gt;Caddy inserts the identity returned by Authentik; and&lt;/li&gt;
&lt;li&gt;Navidrome accepts the header only from Caddy&amp;rsquo;s actual network source.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last point caused the most misleading failure. The address Navidrome sees may be a container address, bridge gateway or host address depending on the network path. It is not necessarily the address used in a browser.&lt;/p&gt;
&lt;p&gt;When the trusted source is wrong, Authentik succeeds and Caddy forwards the header, but Navidrome ignores it and presents its own login screen. Navidrome&amp;rsquo;s log entry about an untrusted source is more useful than guessing.&lt;/p&gt;
&lt;p&gt;Trust the narrowest stable address or CIDR possible. Do not use &lt;code&gt;0.0.0.0/0&lt;/code&gt; simply to make the warning disappear, and do not expose Navidrome directly to an untrusted network while it accepts proxy identity headers.&lt;/p&gt;
&lt;h2 id=&#34;keep-rest-outside-browser-sso&#34;&gt;Keep &lt;code&gt;/rest&lt;/code&gt; outside browser SSO&lt;/h2&gt;
&lt;p&gt;OpenSubsonic clients use Navidrome&amp;rsquo;s &lt;code&gt;/rest&lt;/code&gt; API. They do not complete Authentik&amp;rsquo;s browser redirect flow.&lt;/p&gt;
&lt;p&gt;Caddy therefore needs a narrow exception for the API path before applying forward authentication to browser routes. This does not make the API anonymous: Navidrome still validates the OpenSubsonic username and password.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;/rest&lt;/code&gt; is sent through forward authentication, clients may receive an Authentik page or redirect instead of the expected API response.&lt;/p&gt;
&lt;h2 id=&#34;one-user-two-passwords&#34;&gt;One user, two passwords&lt;/h2&gt;
&lt;p&gt;An account created by the SSO flow can use Authentik in a browser without having a usable Navidrome password. Native clients need a password known to Navidrome.&lt;/p&gt;
&lt;p&gt;The setup for a new user is therefore:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;sign in through Authentik in a browser, creating the Navidrome account;&lt;/li&gt;
&lt;li&gt;set a separate password for that account in Navidrome; and&lt;/li&gt;
&lt;li&gt;use the Navidrome username and password in the OpenSubsonic client.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For Symfonium, select its Subsonic or OpenSubsonic provider. In Feishin, OpenSubsonic mode worked with this arrangement; its Navidrome-native mode followed a different path and conflicted with the proxy SSO configuration.&lt;/p&gt;
&lt;p&gt;The Authentik password should not be entered into those clients. SSO and API credentials serve different protocols and should remain separate.&lt;/p&gt;
&lt;h2 id=&#34;verification-checklist&#34;&gt;Verification checklist&lt;/h2&gt;
&lt;h3 id=&#34;jellyfin&#34;&gt;Jellyfin&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The LDAP outpost is reachable from Jellyfin.&lt;/li&gt;
&lt;li&gt;An approved user can sign in.&lt;/li&gt;
&lt;li&gt;Valid but unauthorised and guest users cannot sign in.&lt;/li&gt;
&lt;li&gt;A wrong password fails.&lt;/li&gt;
&lt;li&gt;Local Jellyfin library and administrator policies remain correct.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;navidrome-browser&#34;&gt;Navidrome browser&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A signed-out browser is redirected to Authentik.&lt;/li&gt;
&lt;li&gt;A successful login reaches the expected Navidrome account.&lt;/li&gt;
&lt;li&gt;A forged client identity header cannot bypass Authentik.&lt;/li&gt;
&lt;li&gt;Navidrome trusts only the source from which Caddy actually connects.&lt;/li&gt;
&lt;li&gt;Direct access is blocked or uses Navidrome&amp;rsquo;s own authentication.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;opensubsonic&#34;&gt;OpenSubsonic&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/rest/*&lt;/code&gt; does not redirect to Authentik.&lt;/li&gt;
&lt;li&gt;The client works with a Navidrome password.&lt;/li&gt;
&lt;li&gt;An incorrect Navidrome password fails.&lt;/li&gt;
&lt;li&gt;The SSO exemption does not include unrelated browser routes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-lesson&#34;&gt;The lesson&lt;/h2&gt;
&lt;p&gt;A shared identity system does not require every application to use the same protocol.&lt;/p&gt;
&lt;p&gt;Jellyfin needs a correctly scoped LDAP provider and outpost. Navidrome&amp;rsquo;s browser interface needs a trusted reverse proxy and carefully controlled identity header. OpenSubsonic clients need Navidrome&amp;rsquo;s native API credentials.&lt;/p&gt;
&lt;p&gt;Once those paths are treated separately, the setup stops feeling like one mysterious login problem and becomes three small, testable boundaries.&lt;/p&gt;
</content>
    </item>
    
  </channel>
</rss>
