From 1db0101b5a5e8e097dce9fb7348ccd078fbe4971 Mon Sep 17 00:00:00 2001 From: Catherine Renelle <32781029+catrenelle@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:57:02 -0500 Subject: [PATCH] Fix SimpleFIN integration for beta-bridge.simplefin.org HttpClient does not extract Basic Auth credentials from URLs with embedded user:pass@host format. Parse the access URL, build the Authorization header explicitly, and strip credentials from the request URI. Also send Content-Length: 0 on token exchange POST and update setup instructions to point to beta-bridge.simplefin.org. Co-Authored-By: Claude Opus 4.6 --- frontend/src/views/settings/SettingsView.vue | 2 +- .../SimpleFinSyncProvider.cs | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/frontend/src/views/settings/SettingsView.vue b/frontend/src/views/settings/SettingsView.vue index acb2402..7dfe909 100644 --- a/frontend/src/views/settings/SettingsView.vue +++ b/frontend/src/views/settings/SettingsView.vue @@ -263,7 +263,7 @@ Connect via SimpleFIN

- Visit simplefin.org/bridge to get a setup token for your bank, then paste it below. + Visit beta-bridge.simplefin.org to get a setup token for your bank, then paste it below.

($"{accessUrl}/accounts"); + var response = await GetWithAuthAsync($"{accessUrl}/accounts"); if (response == null) return new List(); @@ -60,7 +61,7 @@ public class SimpleFinSyncProvider : IAccountSyncProvider var startUnix = new DateTimeOffset(startDate).ToUnixTimeSeconds(); var endUnix = new DateTimeOffset(endDate).ToUnixTimeSeconds(); - var response = await _httpClient.GetFromJsonAsync( + var response = await GetWithAuthAsync( $"{accessUrl}/accounts?start-date={startUnix}&end-date={endUnix}"); if (response == null) @@ -90,6 +91,25 @@ public class SimpleFinSyncProvider : IAccountSyncProvider return result; } + /// + /// HttpClient does not extract Basic Auth credentials from URLs (user:pass@host). + /// This method parses the URI, builds the Authorization header, and strips credentials from the request URL. + /// + private async Task GetWithAuthAsync(string url) + { + var uri = new Uri(url); + var request = new HttpRequestMessage(HttpMethod.Get, new UriBuilder(uri) { UserName = "", Password = "" }.Uri); + if (!string.IsNullOrEmpty(uri.UserInfo)) + { + var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(uri.UserInfo)); + request.Headers.Authorization = new AuthenticationHeaderValue("Basic", credentials); + } + + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync(); + } + private static string GuessAccountType(string name) { var lower = name.ToLowerInvariant();