authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-05 20:33:28-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-08 02:03:19-08:00
logd7aaed393d5ab61468014ee56d2e3393d23beeb7
tree7fe161b043ab877e2a61d70cbfb97792e90ca16c
parent475b7d66a8d2e212107ca5ed624b515b45c90e5e
signaturelock-open Commit is signed but in an unrecognized format.

feat: jellyfin with sso

so this doesn't setup automatic installation of the provider within jellyfin. i frankly do not care enough to get that part of the auth implemented.

5 files changed, 66 insertions(+), 8 deletions(-)

.env.example-1
......@@ -21,7 +21,6 @@ POSTGRES_PASSWORD=
2121POSTGRES_PASSWORD_AUTHELIA=
2222POSTGRES_PASSWORD_FORGEJO=
2323POSTGRES_PASSWORD_DAWARICH=
24POSTGRES_PASSWORD_JELLYFIN=
2524POSTGRES_PASSWORD_LLDAP=
2625
2726CLOVER_SOT_KEY=
compose.yaml+3-3
......@@ -56,8 +56,6 @@ services:
5656 condition: service_started
5757 postgres:
5858 condition: service_healthy
59 redis:
60 condition: service_healthy
6159 healthcheck:
6260 test:
6361 - "CMD-SHELL"
......@@ -242,6 +240,9 @@ services:
242240 net.paperclover.list.domain: jelly
243241 net.paperclover.list.priority: 110
244242 net.paperclover.list.access: media
243 depends_on:
244 keycloak:
245 condition: service_healthy
245246 navidrome: # port 4533
246247 container_name: navidrome
247248 image: deluan/navidrome:latest
......@@ -384,7 +385,6 @@ services:
384385 POSTGRES_PASSWORD_FORGEJO: "${POSTGRES_PASSWORD_FORGEJO:?}"
385386 POSTGRES_PASSWORD_KEYCLOAK: "${POSTGRES_PASSWORD_KEYCLOAK:?}"
386387 POSTGRES_PASSWORD_DAWARICH: "${POSTGRES_PASSWORD_DAWARICH:?}"
387 POSTGRES_PASSWORD_JELLYFIN: "${POSTGRES_PASSWORD_JELLYFIN:?}"
388388 volumes:
389389 - "${APP_ROOT}/pg_data:/var/lib/postgresql/data/"
390390 healthcheck:
config/jellyfin/branding.xml+29-2
......@@ -1,7 +1,34 @@
11<?xml version="1.0" encoding="utf-8"?>
22<BrandingOptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3 <LoginDisclaimer>and then we knew, just like paper airplanes: that we could fly.</LoginDisclaimer>
4 <CustomCss></CustomCss>
3 <LoginDisclaimer>&lt;form action=&quot;/sso/OID/start/snow&quot; id=&quot;snow-sign-on-form&quot;&gt;
4 &lt;h2&gt;you are not logged in...&lt;/h2&gt;
5 &lt;button class=&quot;raised block emby-button button-submit snow-sign-on&quot;&gt;
6 snow sign on
7 &lt;/button&gt;
8&lt;/form&gt;
9
10and then we knew, just like paper airplanes: that we could fly.</LoginDisclaimer>
11 <CustomCss>.loginDisclaimer {width:100%}
12.manualLoginForm, .visualLoginForm, .btnForgotPassword { display: none; }
13.btnQuick {text-transform:lowercase;margin-top: 150px;display:flex}
14#loginPage .emby-button { background: var(--hover-background); width: 100%!important; max-width: 400px!important; margin-left: auto; margin-right: auto; border-radius: 8px; }
15#snow-sign-on-form {transform: translateY(-208px) }
16.snow-sign-on { display: flex; align-items; center; justify-content: center }
17.snow-sign-on:before, .btnQuick:before {
18display: block;
19content: &quot; &quot;;
20width:24px;
21height:24px;
22margin-right: 0.5rem;
23}
24.snow-sign-on:before{
25background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEwIDIwTDguNzUgMTcuNUw2IDE4TTEwIDRMOC43NSA2LjVMNiA2TTE0IDIwTDE1LjI1IDE3LjVMMTggMThNMTQgNEwxNS4yNSA2LjVMMTggNk0xNyAyMUwxNCAxNU0xNCAxNUgxME0xNCAxNUwxNS41IDEyTTEwIDE1TDcgMjFNMTAgMTVMOC41IDEyTTE3IDNMMTQgOU0xNCA5TDE1LjUgMTJNMTQgOUgxME0xNS41IDEySDIyTTIgMTJIOC41TTguNSAxMkwxMCA5TTEwIDlMNyAzTTIwIDEwTDE4LjUgMTJMMjAgMTRNNCAxMEw1LjUgMTJMNCAxNCIgc3Ryb2tlPSJ1cmwoI3BhaW50MF9saW5lYXJfNTQ4XzE5KSIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTEwIDIwTDguNzUgMTcuNUw2IDE4TTEwIDRMOC43NSA2LjVMNiA2TTE0IDIwTDE1LjI1IDE3LjVMMTggMThNMTQgNEwxNS4yNSA2LjVMMTggNk0xNyAyMUwxNCAxNU0xNCAxNUgxME0xNCAxNUwxNS41IDEyTTEwIDE1TDcgMjFNMTAgMTVMOC41IDEyTTE3IDNMMTQgOU0xNCA5TDE1LjUgMTJNMTQgOUgxME0xNS41IDEySDIyTTIgMTJIOC41TTguNSAxMkwxMCA5TTEwIDlMNyAzTTIwIDEwTDE4LjUgMTJMMjAgMTRNNCAxMEw1LjUgMTJMNCAxNCIgc3Ryb2tlPSJ1cmwoI3BhaW50MV9saW5lYXJfNTQ4XzE5KSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl81NDhfMTkiIHgxPSI0IiB5MT0iLTIiIHgyPSIxOC41IiB5Mj0iMjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzIyM0Q5OSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMxNTQzOTIiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzU0OF8xOSIgeDE9IjEwIiB5MT0iLTMiIHgyPSIxOCIgeTI9IjI3IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGMkUzRkYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRjJGOEZGIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+);
26}
27.btnQuick:before{
28mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXNoaWVsZC1lbGxpcHNpcy1pY29uIGx1Y2lkZS1zaGllbGQtZWxsaXBzaXMiPjxwYXRoIGQ9Ik0yMCAxM2MwIDUtMy41IDcuNS03LjY2IDguOTVhMSAxIDAgMCAxLS42Ny0uMDFDNy41IDIwLjUgNCAxOCA0IDEzVjZhMSAxIDAgMCAxIDEtMWMyIDAgNC41LTEuMiA2LjI0LTIuNzJhMS4xNyAxLjE3IDAgMCAxIDEuNTIgMEMxNC41MSAzLjgxIDE3IDUgMTkgNWExIDEgMCAwIDEgMSAxeiIvPjxwYXRoIGQ9Ik04IDEyaC4wMSIvPjxwYXRoIGQ9Ik0xMiAxMmguMDEiLz48cGF0aCBkPSJNMTYgMTJoLjAxIi8+PC9zdmc+);
29mask-size:cover;
30background:var(--main-text);
31}</CustomCss>
532 <SplashscreenEnabled>true</SplashscreenEnabled>
633 <SplashscreenLocation>/config/upload/splashscreen-upload.jpg</SplashscreenLocation>
734</BrandingOptions>
config/keycloak/init.py+34-1
......@@ -39,7 +39,7 @@ realm_config = {
3939 "replyToDisplayName": "clover caruso",
4040 "envelopeFrom": "",
4141 "host": env("MAILER_ADDRESS"),
42 "port": "587",
42 "port": "465",
4343 "ssl": "true",
4444 "starttls": "true",
4545 "auth": "true",
......@@ -161,6 +161,39 @@ def configure():
161161 client_data = kc_admin.get_client(client_id)
162162 _ = Path("/shared/forgejo").write_text(client_data["secret"])
163163
164 # jellyfin
165 client_id = kc_admin.create_client(
166 {
167 "protocol": "openid-connect",
168 "clientId": "jellyfin",
169 "name": "Jellyfin",
170 "description": "",
171 "publicClient": False,
172 "authorizationServicesEnabled": False,
173 "serviceAccountsEnabled": False,
174 "implicitFlowEnabled": False,
175 "directAccessGrantsEnabled": False,
176 "standardFlowEnabled": True,
177 "frontchannelLogout": True,
178 "attributes": {
179 "saml_idp_initiated_sso_url_name": "",
180 "standard.token.exchange.enabled": False,
181 "oauth2.device.authorization.grant.enabled": False,
182 "oidc.ciba.grant.enabled": False,
183 "pkce.code.challenge.method": "",
184 "dpop.bound.access.tokens": "false",
185 "post.logout.redirect.uris": "*",
186 },
187 "alwaysDisplayInConsole": False,
188 "rootUrl": "",
189 "baseUrl": "",
190 "redirectUris": ["*"],
191 },
192 skip_exists=True,
193 )
194 client_data = kc_admin.get_client(client_id)
195 _ = Path("/shared/jellyfin").write_text(client_data["secret"])
196
164197 # forward auth
165198 client_id = kc_admin.create_client(
166199 {
config/postgres/init.sh-1
......@@ -45,7 +45,6 @@ ensure_db_user "clover" "$POSTGRES_PASSWORD"
4545ensure_db_user "keycloak" "$POSTGRES_PASSWORD_KEYCLOAK"
4646ensure_db_user "dawarich" "$POSTGRES_PASSWORD_DAWARICH"
4747ensure_db_user "forgejo" "$POSTGRES_PASSWORD_FORGEJO"
48ensure_db_user "jellyfin" "$POSTGRES_PASSWORD_JELLYFIN"
4948
5049printf "Everything is ready!"
5150