Files
magnus919_agent-skills/jellyfin/references/quick-connect.md
Magnus Hedemarkandfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> 2140d0d58d docs(jellyfin): thicken media-server skill against current API research
Full lastfm-model rebuild of the jellyfin skill against the 12.0-era
OpenAPI spec, core-dev authorization guidance, and server source:

- Document the researched auth sequence end to end: complete pre-token
  Authorization: MediaBrowser Client/Device/DeviceId/Version header
  required by POST /Users/AuthenticateByName (400 "Error processing
  request." without it), AccessToken returned, then Token= on the same
  header (legacy X-Emby-Token deprecated, disableable since 10.11,
  targeted for removal at 12.0).
- Extend scripts/jellyfin: new `login` subcommand demonstrating the
  pre-token header and printing session exports (password via
  stdin/prompt/env only), seasons/episodes TV navigation, next-up
  --series-id, browse --user-id (userId is required on non-API-key
  auth per the ItemsController guard), modern Token= header transport
  with X-Emby-Token fallback, 503 Retry-After handling, search
  Id/deprecated-ItemId fallback.
- Add 5 cited reference files (auth/sessions, endpoint catalog,
  user-scoping matrix, gotchas field guide, worked recipes) plus
  quick-connect; all cite api.jellyfin.org and live-verified sources.
- Upgrade relocated scripts/test_jellyfin_cli.py to the double-runner
  standard: 24 tests (was 8) covering help, argument errors, dry-run,
  mocked login header sequence, TV navigation, search-id fallback, and
  jq-executed pipeline-consumability chains; zero egress proven via
  proxy-trap rerun.
- Add evals/evals.json (6 cases incl. emby-install-not-for-jellyfin
  negative probe); rewrite SKILL.md (224 lines) and README; sync root
  README blurb and skill-triggers row; regenerate marketplace.json and
  llms.txt (description-embedding artifacts).

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-08-29 17:37:35 -04:00

3.4 KiB

Jellyfin quick connect

Quick Connect is Jellyfin's passwordless login: the server displays a short code, the user approves it on a device where they are already signed in, and your client polls until the approval lands. Use it for headless or shared setups where you do not want to handle a password — or when the user account has no password at all (send an empty-string Pw for those in the final exchange).

When Quick Connect is the right flow

  • The CLI runs where you cannot (or should not) type a password: CI, SSH sessions, cron.
  • You do not want the script to ever see the user's password.
  • The server has Quick Connect enabled — otherwise POST /QuickConnect/Initiate answers 401 with "Quick connect is not active on this server" (that 401 is the disable signal, not an auth failure).

The flow

1. POST /QuickConnect/Initiate                 # no authentication required
   → 200 { "Secret": "<secret>", "Code": "123456", "Authenticated": false }
   → 401 = feature disabled on this server

2. Show the Code to the user; on another signed-in client they approve it
   (Dashboard or the client prompt).

3. Poll every ~5 seconds:
   GET /QuickConnect/Connect?secret={Secret}   # quick-connect state
   → QuickConnectResult with Authenticated flipping to true when approved

4. POST /Users/AuthenticateWithQuickConnect    # body: {"Secret": "<secret>"}
   → 200 AuthenticationResult                  # SAME capture as password login

AuthenticationResult is identical to the password flow's: capture User.Id as USER_ID and AccessToken as TOKEN, then send the standard Authorization: MediaBrowser ... Token="..." header on every subsequent call. The same session rules apply — one token per (DeviceId, user) pair, re-login revokes the pair's previous token — so still send a complete Client/Device/DeviceId/Version header with the AuthenticateWithQuickConnect call.

Error paths: 400 "Missing token" on step 4 when Secret is absent; the 401-on-initiate disable case above; polling forever if the user never approves — bound your loop.

Which login flow should my client use?

Situation Flow
Scripting with a persistent admin credential API key from Dashboard → API Keys (Authorization: MediaBrowser Token="<key>")
Interactive one-user session POST /Users/AuthenticateByName with the full pre-token MediaBrowser header
Headless / passwordless / shared device Quick Connect (this reference)
Server with legacy auth disabled and a very old client Nothing helps — upgrade the client to speak the modern header

Sources