Jellyfin SSO with Authentik (OIDC)
Jellyfin uses the SSO-Auth plugin with OpenID Connect for true single sign-on with Authentik. This provides automatic login - no separate credentials needed.
Key Difference from arr-stack: Jellyfin supports native OIDC via plugin, so we use that instead of forward auth. This gives true SSO (one-click login) rather than just access control.
Architecture
User → jellyfin.bogocat.com → K8s Ingress
│
▼
Jellyfin (LXC 113)
│
┌──────────┴──────────┐
│ │
(not logged in) (logged in)
│ │
▼ ▼
Click "Sign in with SSO" Access granted
│
▼
Redirect to Authentik
│
▼
Login (or auto if cached)
│
▼
Redirect back to Jellyfin
(auto-creates user, sets admin based on groups)
Prerequisites
- Authentik deployed in K8s (
authentik namespace)
- Jellyfin running (LXC 113: 10.89.97.97)
- DNS:
jellyfin.bogocat.com → 10.89.97.220
- Tier groups configured in Authentik (
tier-owner, tier-family, tier-friends)
Role-Based Access
| Tier Group |
Jellyfin Role |
Access Level |
tier-owner |
Admin |
Full admin access |
tier-family |
User |
All libraries |
tier-friends |
User |
All libraries |
tier-guests |
(not configured) |
No access |
Setup Guide
Step 1: Create OIDC Provider in Authentik
- Go to https://auth.bogocat.com → Providers → Create → OAuth2/OpenID Provider
- Configure:
| Field |
Value |
| Name |
jellyfin |
| Authorization flow |
default-provider-authorization-implicit-consent |
| Redirect URI |
https://jellyfin.bogocat.com/sso/OID/redirect/authentik |
- Note the Client ID and Client Secret
Step 2: Create Application in Authentik
- Applications → Create
- Configure:
| Field |
Value |
| Name |
Jellyfin |
| Slug |
jellyfin |
| Provider |
jellyfin |
| Launch URL |
https://jellyfin.bogocat.com/sso/OID/start/authentik |
- Policy / Group / User Bindings → Create Binding → Group
- Add:
tier-owner, tier-family, tier-friends
Step 3: Create Groups Scope in Authentik
- Customization → Property Mappings → Create → Scope Mapping
- Configure:
| Field |
Value |
| Name |
groups |
| Scope name |
groups |
| Expression |
return list(request.user.ak_groups.values_list("name", flat=True)) |
- Providers → jellyfin → Edit → Advanced Protocol Settings
- Add the
groups scope to Scopes
Step 4: Install SSO-Auth Plugin in Jellyfin
- Login to Jellyfin as admin
- Dashboard → Plugins → Repositories → Add (+)
| Field |
Value |
| Name |
SSO-Auth |
| URL |
https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json |
- Catalog → SSO-Auth → Install
- Restart Jellyfin:
pct exec 113 -- systemctl restart jellyfin
- Dashboard → Plugins → SSO-Auth
- Configure:
| Field |
Value |
| Name of OID Provider |
authentik |
| OID Endpoint |
https://auth.bogocat.com/application/o/jellyfin/ |
| OpenID Client ID |
(from Authentik) |
| OID Secret |
(from Authentik) |
| Enabled |
✅ |
| Enable Authorization by Plugin |
✅ |
| Enable All Folders |
✅ |
| Roles |
tier-owner
tier-family
tier-friends |
| Admin Roles |
tier-owner |
| Role Claim |
groups |
| Request Additional Scopes |
groups |
- Save and restart Jellyfin
- Dashboard → General → Login Disclaimer
- Add:
<form action="https://jellyfin.bogocat.com/sso/OID/start/authentik">
<button class="raised block emby-button button-submit">
Sign in with SSO
</button>
</form>
- Save
Step 7: Create K8s Ingress
The ingress is a simple passthrough (no forward auth needed since Jellyfin handles OIDC itself):
# /root/tower-fleet/manifests/apps/jellyfin/ingress.yaml
---
apiVersion: v1
kind: Service
metadata:
name: jellyfin-external
namespace: arr-stack
labels:
app.kubernetes.io/managed-by: tower-fleet
app.kubernetes.io/name: jellyfin
spec:
type: ClusterIP
clusterIP: None
ports:
- name: http
port: 8096
targetPort: 8096
---
apiVersion: v1
kind: Endpoints
metadata:
name: jellyfin-external
namespace: arr-stack
subsets:
- addresses:
- ip: 10.89.97.97
ports:
- name: http
port: 8096
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jellyfin
namespace: arr-stack
labels:
app.kubernetes.io/managed-by: tower-fleet
app.kubernetes.io/name: jellyfin
annotations:
nginx.ingress.kubernetes.io/backend-protocol: HTTP
spec:
ingressClassName: nginx
tls:
- hosts:
- jellyfin.bogocat.com
secretName: wildcard-bogocat-tls
rules:
- host: jellyfin.bogocat.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jellyfin-external
port:
number: 8096
Apply:
kubectl apply -f /root/tower-fleet/manifests/apps/jellyfin/ingress.yaml
Step 8: Test
- Open incognito browser
- Go to https://jellyfin.bogocat.com
- Click "Sign in with SSO"
- Authenticate with Authentik (or auto-login if session exists)
- Redirected back to Jellyfin, logged in automatically
- If user is in
tier-owner, they have admin privileges
Troubleshooting
"Client ID Error" on redirect
- Verify Client ID in Jellyfin matches Authentik provider exactly
- Check for extra spaces or wrong ID
"Permission denied" after Authentik login
- User not in a group bound to the application
- Add user to
tier-owner, tier-family, or tier-friends
User created but not admin
- Check
Admin Roles includes tier-owner
- Check
Role Claim is set to groups
- Check
Request Additional Scopes includes groups
- Verify groups scope is added to provider in Authentik
"Error processing request" / endpoint error
- Enable "Do not validate endpoints" in plugin settings
- Check OID Endpoint URL ends with
/
LDAP vs OIDC
Jellyfin previously used LDAP authentication. Here's why we switched:
| Feature |
LDAP |
OIDC (SSO Plugin) |
| Auto-login |
❌ Must type credentials |
✅ One-click if Authentik session exists |
| Role-based admin |
❌ Manual configuration |
✅ Automatic via groups claim |
| User creation |
✅ Automatic |
✅ Automatic |
| Session sharing |
❌ Separate session |
✅ Shares Authentik session |
The LDAP provider can be disabled in Jellyfin plugins if no longer needed.
Files Reference
| File |
Purpose |
/root/tower-fleet/manifests/apps/jellyfin/ingress.yaml |
K8s ingress (passthrough) |
/root/tower-fleet/docs/infrastructure/jellyfin-sso.md |
This documentation |