Skip to content

Send the Jellyfin download token in the Authorization header - #1603

Merged
GianniCarlo merged 2 commits into
TortugaPower:developfrom
pablofontanilla:fix/jellyfin-download-auth
Oct 5, 2026
Merged

GianniCarlo merged 2 commits into
TortugaPower:developfrom
pablofontanilla:fix/jellyfin-download-auth

Conversation

@pablofontanilla

@pablofontanilla pablofontanilla commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Refs #1598. Jellyfin audiobook downloads fail with HTTP 401.

createItemDownloadRequest sent the access token only as an ?api_key= query item. Our Jellyfin 12.1.0 server rejects that with a 401. This PR:

  • sets Authorization: MediaBrowser Token="…" on the download request. It is set after wrapWithCustomHeaders, so a custom header cannot replace it (same rule as JellyfinHeaderInjector).
  • removes the ?api_key= query item. This also keeps the token out of the download task's persisted taskDescription (SingleFileDownload-<url>).

No new files and no project.pbxproj changes. The tests are in the existing JellyfinQuickConnectTests.swift.

Header test on our 12.1.0 server

curl, same item, same API key:

Auth method /Items/{id}/Download (range) /Users/{uid}/Items/{id}
?api_key= 401 –
X-Emby-Token: <key> 401 401
X-MediaBrowser-Token: <key> – 401
X-Emby-Authorization: MediaBrowser Token="<key>" – 401
Authorization: MediaBrowser Token="<key>" 206 200

We did not check whether this is a 12.x default or a server setting.

Overlap with #1586

#1586 also fixes this download path, with X-Emby-Token and no ?api_key=. On our server, X-Emby-Token also gets a 401 (see the table), so #1586 may need the Authorization header too.

@GianniCarlo, you can pick the merge order. If this PR merges first, keep your version of createItemDownloadRequest when you rebase and change the header. If you prefer, I can rebase this PR onto #1586.

Out of scope

#1598 also says that Quick Connect does not work. This PR does not change Quick Connect.

Tests

  • New JellyfinDownloadRequestTests (2 tests): the token is in the Authorization header, and the URL has no token and no api_key.
  • Unit Tests plan (BookPlayerTests), iPhone 17 simulator: 535/535 pass.
  • Manual test on the 12.1.0 server (iPhone 17 simulator): an 807 MB audiobook downloaded and imported. There was no 401. (Before the fix, the same request with ?api_key= returned 401 with curl.)

🤖 Generated with Claude Code

The download request put the access token only in an `?api_key=` query
item. A Jellyfin 12.1.0 server rejects that with a 401, so downloads
failed. The request now sets `Authorization: MediaBrowser Token="…"`
after the custom headers, so a custom header cannot replace it.

The `?api_key=` query item is removed, so the token does not go into the
download task's persisted `taskDescription`.

Refs TortugaPower#1598

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
createItemDownloadUrl no longer carries the token, so the URL alone gets a
401 on every Jellyfin version. Make it private with the same warning the
AudiobookShelf builder has, so new callers go through
createItemDownloadRequest, which adds the Authorization header. The client
guard is gone: createUrlComponentsForApiRequest already throws noClient.

The Sentry query-stripping comment no longer lists Jellyfin download URLs;
AudiobookShelf's OIDC query still needs the stripping.
@GianniCarlo
GianniCarlo merged commit 6a5f9ed into TortugaPower:develop Oct 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants