fix(auth): use the authentication spellings Jellyfin 12.0 leaves enabled

X-Emby-Authorization at the remaining request builders, and api_key= in the
player-facing URLs, become Authorization and ApiKey.

Jellyfin 12.0 disables X-Emby-Authorization, X-Emby-Token, X-MediaBrowser-Token,
the Emby scheme and the api_key query parameter by default — and a migration
(DisableLegacyAuthorization) turns them off on servers upgraded from 10.11 as
well, so this is not confined to fresh installs. A client using them stops
working against an upgraded server rather than degrading.

Verified at source level rather than inferred: AuthorizationContext.cs is
byte-identical between v10.11.5 and v12.0 apart from whitespace. The only change
is the default of the gate that guards the legacy spellings. Authorization with
the MediaBrowser scheme, and ApiKey as a query parameter, are ungated in both
trees — and the server itself emits ApiKey in both (StreamInfo.cs). So one
spelling is correct everywhere and no capability flag is involved.

Also adds ServerCompatibility to ServerInfo: an opaque verdict the frontend
renders without ever comparing a version number, with three states rather than a
boolean. A server newer than this build is usable, not refused; an unreadable
version string is not grounds for refusal either. Only a server below the floor
is refused.

TRACES: UR-085 | DR-286, DR-287

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-08 20:09:34 +02:00
co-authored by Claude Opus 5
parent 8027fd5fac
commit 9bb5b44d0f
6 changed files with 187 additions and 20 deletions
+9 -6
View File
@@ -54,7 +54,10 @@ impl JellyfinClient {
return "Unknown";
}
/// Build the X-Emby-Authorization header value
/// Build the value for the `Authorization` header (the `MediaBrowser`
/// scheme — see `HttpClient::build_auth_header`).
///
/// TRACES: UR-085 | DR-287
fn get_auth_header(&self) -> String {
format!(
"MediaBrowser Client=\"{}\", Version=\"{}\", Device=\"{}\", DeviceId=\"{}\", Token=\"{}\"",
@@ -75,7 +78,7 @@ impl JellyfinClient {
let response = self
.http_client
.get(&url)
.header("X-Emby-Authorization", self.get_auth_header())
.header("Authorization", self.get_auth_header())
.send()
.await
.map_err(|e| {
@@ -155,7 +158,7 @@ impl JellyfinClient {
.http_client
.post(&url)
.header("Content-Type", "application/json")
.header("X-Emby-Authorization", self.get_auth_header())
.header("Authorization", self.get_auth_header())
.json(body)
.send()
.await
@@ -293,7 +296,7 @@ impl JellyfinClient {
let response = self
.http_client
.post(&url)
.header("X-Emby-Authorization", self.get_auth_header())
.header("Authorization", self.get_auth_header())
.send()
.await
.map_err(|e| {
@@ -360,7 +363,7 @@ impl JellyfinClient {
let response = self
.http_client
.post(&url)
.header("X-Emby-Authorization", self.get_auth_header())
.header("Authorization", self.get_auth_header())
.send()
.await
.map_err(|e| format!("Network request failed: {}", e))?;
@@ -503,7 +506,7 @@ impl JellyfinClient {
let response = self
.http_client
.delete(&url)
.header("X-Emby-Authorization", self.get_auth_header())
.header("Authorization", self.get_auth_header())
.send()
.await
.map_err(|e| format!("Network request failed: {}", e))?;