Fix three basic-auth regressions surfaced by end-to-end testing

- InstanceDetector: a single 401 from one probe was over-eagerly concluded
  as "credentials invalid" / "credentials required". On instances behind a
  reverse proxy where one probe path (e.g. Yattee Server's /info) hits a
  same-origin redirect, iOS URLSession strips the Authorization header on
  the redirect and the request 401s even with valid credentials. Track 401s
  across all probes and only conclude basicAuthRequired/basicAuthInvalid
  when no probe matched and at least one returned 401.

- InstanceLoginView: the Invidious/Piped login flow constructed an API
  client backed by the shared appEnvironment.httpClient, which has no
  per-instance basic-auth headers. For instances behind a reverse proxy,
  the login POST 401d before reaching the upstream login endpoint. Build a
  per-instance HTTPClient with the basic-auth Authorization header baked in
  via setDefaultHeaders, mirroring ContentService.httpClientWithBasicAuth.

- InvidiousAPI.login: the login function constructs its own URLSession (to
  capture Set-Cookie via a redirect-blocking delegate), so it never
  inherits headers from the injected httpClient. Add an optional
  extraHeaders parameter and have InstanceLoginView pass the basic-auth
  header through when present. PipedAPI.login uses httpClient.fetch and
  inherits defaultHeaders correctly, so no change is needed there.
This commit is contained in:
Arkadiusz Fal
2026-04-06 22:11:55 +02:00
parent 3dd4073db7
commit eefd49f743
3 changed files with 54 additions and 12 deletions

View File

@@ -348,7 +348,7 @@ actor InvidiousAPI: InstanceAPI {
/// - password: The user's password
/// - instance: The Invidious instance to log in to
/// - Returns: The session ID (SID) cookie value
func login(email: String, password: String, instance: Instance) async throws -> String {
func login(email: String, password: String, instance: Instance, extraHeaders: [String: String]? = nil) async throws -> String {
// Build form-urlencoded body using URLComponents for standard encoding
var components = URLComponents()
components.queryItems = [
@@ -370,6 +370,16 @@ actor InvidiousAPI: InstanceAPI {
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpBody = bodyData
// Apply any extra headers (e.g. an HTTP Basic Auth Authorization header
// for instances behind a reverse proxy). The login endpoint uses its own
// URLSession below to capture Set-Cookie, so it cannot inherit headers
// from the injected httpClient.
if let extraHeaders {
for (key, value) in extraHeaders {
request.setValue(value, forHTTPHeaderField: key)
}
}
// Use a session that doesn't follow redirects so we can capture the Set-Cookie header
let sessionConfig = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: sessionConfig, delegate: RedirectBlocker(), delegateQueue: nil)