Everything since v1.0.0-beta.2: an OIDC security overhaul, plus fixes to search accuracy, the organizer, library sync and the request flow.
Security — OIDC
ID tokens are now fully validated. The issuer is pinned to the configured provider, only asymmetric algorithms are accepted (HS256 is gone — it cannot verify against a JWKS and invites a key-confusion attack), and exp/iat/iss/sub/aud are required rather than checked only when present. azp/aud consistency is checked explicitly, and at_hash is verified when the provider sends it (#1).
Account linking now keys exclusively on the immutable (iss, sub) pair. Linking by preferred_username or by unverified email is removed — either allowed a federated user to bind onto an existing local account and inherit its role (#13). email_verified must be boolean true; the string "false" no longer passes a truthiness test.
Additional hardening: the OIDC state is signed and carried in a __Host- cookie and validated on callback (CSRF / mix-up defence); the callback fails closed with a generic error that reflects no provider text; session cookies are marked Secure on direct TLS requests, not only behind a proxy; python-jose 3.3.0 → 3.4.0 (CVE-2024-33664 / CVE-2024-33663).
redirect_uri is always absolute — preferring public_url, otherwise inferred from the proxy's forwarded headers — and is computed once at /start and carried to the callback so the two always match (#2).
Search accuracy
Auto-search scoring now rejects same-series wrong books. Two new penalties cap the score below the auto-download threshold: when a third or more of the query's meaningful title words are absent from a result, and when extra meaningful words precede the query's first keyword. Together these stop "Call of the Bone Ships" being downloaded for a search for "The Bone Ships".
Prowlarr query building is fixed for two classes of zero-result search: hyphens are stripped from titles (some indexers read a leading hyphen as NOT, turning "No-Drama" into No AND NOT Drama), and surname extraction skips credentials and initials, so "Daniel J. Siegel, MD" yields "Siegel" rather than "MD".
Organizer
Torrent files are copied rather than moved. get_torrent_client() returns a downloader id such as qbittorrent-1, so the organizer's hardcoded comparison against "qbittorrent" never matched and every torrent was moved out of its download directory, breaking seeding. Client references now resolve through is_torrent_client(), which handles both ids and legacy type strings.
Organize now overwrites a same-extension file instead of skipping it, and deletes stale files of that extension after placing; other extensions are left alone.
auto_organize no longer returns silently when download_path is unset — the request is marked failed with a reason, so the failure is visible in the UI rather than only in the logs (#7).
Library sync and requests
Actively-downloading requests are no longer deleted by library sync; cleanup is limited to orphaned requested, completed and in_library entries. A failed request resolves to in_library when sync finds that format in ABS.
Admins can re-run a search against an in-library format to replace an incorrect download, bypassing in-library dedup and suppressing auto-search. The replace path clears stuck requests instead of blocking, and only blocks when a download is genuinely mid-flight.
Non-admins no longer get a 500 when filtering the request list by status. The filter discarded the ownership condition while leaving its binding in place, so the query ran with one placeholder against two bindings; had the counts lined up it would have shown a non-admin every user's requests (#4).
Interface
A hide-unreleased toggle on the request queue, persisted in the URL hash and filtered client-side so it applies without a re-fetch. Failed-request badges now appear in series list view and format pills. Series-pack queries strip leading articles.
Upgrading
Migration 15 adds users.oidc_iss with UNIQUE(oidc_iss, oidc_sub). Accounts linked before this release are adopted on their next login, keeping their id and role — no action needed, and no duplicate accounts are created. Everyone will be asked to sign in again, as the state cookie changed.
With thanks to @fmaass, who reported the OIDC issues and contributed the fixes.