Extension OAuth + store: flatten action JSON, open auth URLs, spotiflac:// callback

Third-party extensions (e.g. Spotify PKCE addons) need three things the current app does not fully provide:

Extension button results – The Go runtime returned { success, result: { message, open_auth_url, … } } while Flutter read message / open_auth_url only on the outer map, so OAuth buttons appeared to do nothing. InvokeAction now merges the extension’s return object onto the top-level JSON (arrays/non-objects still use result).

Flutter – extension_detail_page: unwrap nested result for compatibility, merge setting_updates into saved extension settings (for copyable OAuth URLs), and launchUrl when open_auth_url is set.

Mobile OAuth return – spotiflac://callback?code=…&state=<extension_id> was not handled on Android (manifest + MainActivity) or iOS (AppDelegate open URL + cold-start launchOptions). This wires SetExtensionAuthCodeByID + invokeExtensionAction(..., "completeSpotifyLogin") so PKCE extensions can finish login after the browser redirect.

Extension store HTTP – Add Cache-Control: no-cache on registry and extension package downloads to reduce stale CDN/proxy responses.

Testing: Install a metadata extension that uses PKCE; tap Connect; confirm browser opens, return via spotiflac://callback, and tokens complete without pasting the code manually.

extension InvokeAction JSON was nested under result while the Flutter settings UI only read the top level, so OAuth-related buttons never showed messages or opened the browser. This PR flattens that payload, merges optional setting_updates, launches open_auth_url, adds spotiflac://callback handling on Android and iOS, and sends no-cache on store HTTP fetches. Needed for extensions like SpoitiLists (Spotify Web API + PKCE).
This commit is contained in:
Alex
2026-04-12 02:40:31 -06:00
parent ee5b3824e9
commit 3fc371b8c4
6 changed files with 242 additions and 12 deletions
+46
View File
@@ -66,9 +66,55 @@ import Gobackend // Import Go framework
)
GeneratedPluginRegistrant.register(with: self)
if let url = launchOptions?[.url] as? URL {
handleExtensionOAuthRedirect(url: url)
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
/// PKCE OAuth return URL: spotiflac://callback?code=...&state=<extension_id>
private func handleExtensionOAuthRedirect(url: URL) {
guard let scheme = url.scheme?.lowercased(), scheme == "spotiflac" else { return }
let host = (url.host ?? "").lowercased()
let path = url.path.lowercased()
let ok =
host == "callback" || host == "spotify-callback" || path.contains("callback")
guard ok else { return }
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return
}
let q = components.queryItems ?? []
let code =
q.first { $0.name == "code" }?.value?.trimmingCharacters(
in: .whitespacesAndNewlines) ?? ""
let state =
q.first { $0.name == "state" }?.value?.trimmingCharacters(
in: .whitespacesAndNewlines) ?? ""
if code.isEmpty { return }
if state.isEmpty {
NSLog("SpotiFLAC: Extension OAuth redirect missing state (extension id)")
return
}
streamQueue.async {
var err: NSError?
GobackendSetExtensionAuthCodeByID(state, code)
_ = GobackendInvokeExtensionActionJSON(state, "completeSpotifyLogin", &err)
if let err = err {
NSLog(
"SpotiFLAC: Extension OAuth complete failed: \(err.localizedDescription)")
}
}
}
override func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
handleExtensionOAuthRedirect(url: url)
return super.application(app, open: url, options: options)
}
deinit {
stopDownloadProgressStream()
stopLibraryScanProgressStream()