From 11841d7b416d4bd74146d2f1f387d2f62f2569bf Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Wed, 6 May 2026 20:17:09 +0200 Subject: [PATCH] Send Piped session token in Authorization header again Commit aed78c13f moved the session token from the Authorization header to a ?authToken= query parameter on /subscriptions, /subscribe, /unsubscribe, /user/playlists, and /playlists/{id}. Piped's backend only accepts the ?authToken= form on /feed; every other authenticated route reads it from Authorization (the Java handler names the variable "session", which is why the rejected requests returned "session is a required parameter"). Restore the header form for those five routes and leave feed alone. --- Yattee/Services/API/PipedAPI.swift | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Yattee/Services/API/PipedAPI.swift b/Yattee/Services/API/PipedAPI.swift index 4943542a..06489b48 100644 --- a/Yattee/Services/API/PipedAPI.swift +++ b/Yattee/Services/API/PipedAPI.swift @@ -297,12 +297,10 @@ actor PipedAPI: InstanceAPI { /// - authToken: The auth token from login /// - Returns: Array of subscribed channels func subscriptions(instance: Instance, authToken: String) async throws -> [PipedSubscription] { - // Piped accepts the auth token via the `authToken` query parameter, which keeps the - // `Authorization` header free for HTTP Basic Auth from a fronting reverse proxy. let endpoint = GenericEndpoint( path: "/subscriptions", method: .get, - queryItems: [URLQueryItem(name: "authToken", value: authToken)] + headers: ["Authorization": authToken] ) let response: [PipedSubscription] = try await httpClient.fetch(endpoint, baseURL: instance.url) return response @@ -322,8 +320,7 @@ actor PipedAPI: InstanceAPI { let endpoint = GenericEndpoint( path: "/subscribe", method: .post, - queryItems: [URLQueryItem(name: "authToken", value: authToken)], - headers: ["Content-Type": "application/json"], + headers: ["Authorization": authToken, "Content-Type": "application/json"], body: bodyData ) @@ -345,8 +342,7 @@ actor PipedAPI: InstanceAPI { let endpoint = GenericEndpoint( path: "/unsubscribe", method: .post, - queryItems: [URLQueryItem(name: "authToken", value: authToken)], - headers: ["Content-Type": "application/json"], + headers: ["Authorization": authToken, "Content-Type": "application/json"], body: bodyData ) @@ -362,7 +358,7 @@ actor PipedAPI: InstanceAPI { let endpoint = GenericEndpoint( path: "/user/playlists", method: .get, - queryItems: [URLQueryItem(name: "authToken", value: authToken)] + headers: ["Authorization": authToken] ) let response: [PipedUserPlaylist] = try await httpClient.fetch(endpoint, baseURL: instance.url) return response.map { $0.toPlaylist() } @@ -375,11 +371,11 @@ actor PipedAPI: InstanceAPI { /// - authToken: The auth token from login /// - Returns: Playlist with videos func userPlaylist(id: String, instance: Instance, authToken: String) async throws -> Playlist { - let authQueryItem = URLQueryItem(name: "authToken", value: authToken) + let headers = ["Authorization": authToken] let firstEndpoint = GenericEndpoint( path: "/playlists/\(id)", method: .get, - queryItems: [authQueryItem] + headers: headers ) let firstResponse: PipedPlaylistResponse = try await httpClient.fetch(firstEndpoint, baseURL: instance.url) var allStreams = firstResponse.relatedStreams ?? [] @@ -390,7 +386,8 @@ actor PipedAPI: InstanceAPI { while let token = nextpage, page < maxPages { let nextEndpoint = GenericEndpoint( path: "/nextpage/playlists/\(id)", - queryItems: [URLQueryItem(name: "nextpage", value: token), authQueryItem] + queryItems: [URLQueryItem(name: "nextpage", value: token)], + headers: headers ) let nextResponse: PipedPlaylistNextPageResponse = try await httpClient.fetch(nextEndpoint, baseURL: instance.url) let pageStreams = nextResponse.relatedStreams ?? []