Skip to content

Support qBittorrent 5.2 API key authentication - #367

Merged
jrhager84 merged 1 commit into
ManiMatter:devfrom
jrhager84:qbit-api-key-353
Jul 13, 2026
Merged

Support qBittorrent 5.2 API key authentication#367
jrhager84 merged 1 commit into
ManiMatter:devfrom
jrhager84:qbit-api-key-353

Conversation

@jrhager84

Copy link
Copy Markdown
Collaborator

Summary

Adds support for qBittorrent 5.2's stateless API key auth (Authorization: Bearer <key>) and makes it the recommended way to connect. Username and password stay as a legacy fallback for qBit older than 5.2.

Closes #353.

Behavior

  • Set api_key on a qbittorrent client and decluttarr sends Authorization: Bearer <key> on every request. No login call, no session cookie.
  • If both api_key and username/password are set, the key wins (matching qBit's own precedence) and a one time info log notes the credentials are ignored.
  • Requires qBit 5.2.0 or newer. A bad key or a too old server both return 403, which degrades that instance with a tip covering both causes rather than crash looping the container (builds on Degrade unreachable instances at startup instead of exiting the whole app #366).

Security

  • Authorization is added to the log redaction set, so the Bearer token is never written to debug logs.

Also

  • check_connected now includes the underlying error in its warning, so a key revoked at runtime surfaces the auth failure instead of a generic "disconnected" line.

Testing

  • New tests cover key mode vs password mode request shaping, the reachability probe, precedence when both are set, 403 degradation with the tip, and token redaction.
  • Full suite green (the 2 Windows path failures are pre existing on dev and unrelated).
  • Validated on live hardware: a correct key connects and runs cleanly; a wrong key degrades gracefully with the tip, the app stays up, and no protected or private torrents are touched.

self.name = name
if not self.name:
logger.verbose(
"No name provided for qbittorrent client, assuming 'qBitorrent'. If the name used in your *arr is different, please correct either the name in your *arr, or set the name in your config",
)
self.name = "qBittorrent"

if self.api_key and (username or password):

@ManiMatter ManiMatter Jul 12, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to also add
a) an error that qbit needs to be updated for api key to be supported (if api_key and qbitversion < 5.2)?
b) a recommendation to switch from password to api key (if (username or password) and qbitversion >= 5.2)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, both are useful, with one limitation:
a) If api_key is configured and we successfully retrieve a qBittorrent version below 5.2, we should warn that the configured API key is unsupported and is not providing authentication. We should not fail the application or remove support for older qBittorrent versions.
If the initial Bearer request returns 403, we cannot retrieve the version because /app/version itself requires authentication. That response can mean either an invalid key on 5.2+ or an older server without API-key support, so the existing combined troubleshooting message remains necessary.
b) If username/password is configured and the retrieved version is 5.2+, we can reliably log an informational recommendation to switch to an API key while continuing normally.
I’ll add both post-version guidance checks without changing the existing qBittorrent 4.3 minimum or making API keys mandatory.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry - forgot to post that earlier. Anywho:

Updated in 701f67f. Added the post-version warning for API key + qBittorrent below 5.2 and the recommendation for username/password + qBittorrent 5.2+. These are guidance-only and do not change supported versions or authentication behavior. The existing ambiguous-403 handling remains unchanged. All 37 relevant tests pass. Ready for re-review.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

qBittorrent 5.2 added stateless API-key auth (Authorization: Bearer),
which lets users avoid storing username/password credentials. This adds
an optional `api_key` field to qBittorrent clients and makes it the
recommended auth path; username/password remain as a legacy fallback for
qBit < 5.2.

- QbitClient accepts `api_key`; a new `_auth_kwargs()` helper returns the
  Bearer header in key mode or the SID cookie in password mode, used at
  every authenticated request site.
- refresh_cookie() and check_qbit_reachability() branch for key mode:
  login is skipped (qBit rejects /auth/login under key auth) and
  reachability probes /app/version with the Bearer header instead. A
  bad key or a <5.2 server both surface a clear tip and degrade instead
  of crashing.
- If both api_key and username/password are set, the key wins (mirrors
  qBit), logged once at init.
- Redact `Authorization` in sanitize_kwargs so the token never leaks in
  DEBUG logs.
- Docs (config example, README) present api_key as recommended, creds as
  legacy. Tests cover header-vs-cookie selection, login skip, precedence,
  and the 403 degrade path.
@jrhager84
jrhager84 requested a review from ManiMatter July 13, 2026 06:45

@lolimmlost lolimmlost left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean implementation. The _auth_kwargs() abstraction is the right level of indirection -- encapsulates auth mode in one place and every request site just spreads it. Complete substitution of all 13 cookie call sites verified.

Key points:

  • Key mode correctly skips /auth/login and probes /app/version instead; response = None safely bypasses the "Fails." password check via getattr guard
  • Token redaction in sanitize_kwargs handles nested dicts (headers > Authorization) correctly
  • Whitespace stripping on api_key prevents sending empty Bearer tokens
  • Precedence (key wins over creds) matches qBit's own behavior
  • Version guidance (701f67f) is guidance-only, no behavior changes -- warns on api_key + <5.2, recommends api_key on password + >=5.2, silent otherwise
  • Test coverage is thorough across all auth paths, version guidance branches, degradation, and redaction

LGTM.

@jrhager84
jrhager84 merged commit 6564212 into ManiMatter:dev Jul 13, 2026
4 checks 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.

Support API Token to authenticate with qBit (instead of username/PW) in qbit 5.2.X

3 participants