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 <noreply@anthropic.com>
This commit is contained in:
@@ -263,7 +263,7 @@
|
|||||||
<v-card-title>Connect via SimpleFIN</v-card-title>
|
<v-card-title>Connect via SimpleFIN</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<p class="mb-4">
|
<p class="mb-4">
|
||||||
Visit <strong>simplefin.org/bridge</strong> to get a setup token for your bank, then paste it below.
|
Visit <strong>beta-bridge.simplefin.org</strong> to get a setup token for your bank, then paste it below.
|
||||||
</p>
|
</p>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="simpleFinToken"
|
v-model="simpleFinToken"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Purrse.Plugins.Abstractions;
|
using Purrse.Plugins.Abstractions;
|
||||||
@@ -28,7 +29,7 @@ public class SimpleFinSyncProvider : IAccountSyncProvider
|
|||||||
// Decode base64 setup token to get the claim URL
|
// Decode base64 setup token to get the claim URL
|
||||||
var claimUrl = Encoding.UTF8.GetString(Convert.FromBase64String(setupToken));
|
var claimUrl = Encoding.UTF8.GetString(Convert.FromBase64String(setupToken));
|
||||||
|
|
||||||
var response = await _httpClient.PostAsync(claimUrl, null);
|
var response = await _httpClient.PostAsync(claimUrl, new StringContent(string.Empty));
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var accessUrl = await response.Content.ReadAsStringAsync();
|
var accessUrl = await response.Content.ReadAsStringAsync();
|
||||||
@@ -39,7 +40,7 @@ public class SimpleFinSyncProvider : IAccountSyncProvider
|
|||||||
{
|
{
|
||||||
var accessUrl = configuration.GetValueOrDefault("AccessUrl") ?? string.Empty;
|
var accessUrl = configuration.GetValueOrDefault("AccessUrl") ?? string.Empty;
|
||||||
|
|
||||||
var response = await _httpClient.GetFromJsonAsync<SimpleFinAccountSet>($"{accessUrl}/accounts");
|
var response = await GetWithAuthAsync($"{accessUrl}/accounts");
|
||||||
if (response == null)
|
if (response == null)
|
||||||
return new List<SyncAccount>();
|
return new List<SyncAccount>();
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ public class SimpleFinSyncProvider : IAccountSyncProvider
|
|||||||
var startUnix = new DateTimeOffset(startDate).ToUnixTimeSeconds();
|
var startUnix = new DateTimeOffset(startDate).ToUnixTimeSeconds();
|
||||||
var endUnix = new DateTimeOffset(endDate).ToUnixTimeSeconds();
|
var endUnix = new DateTimeOffset(endDate).ToUnixTimeSeconds();
|
||||||
|
|
||||||
var response = await _httpClient.GetFromJsonAsync<SimpleFinAccountSet>(
|
var response = await GetWithAuthAsync(
|
||||||
$"{accessUrl}/accounts?start-date={startUnix}&end-date={endUnix}");
|
$"{accessUrl}/accounts?start-date={startUnix}&end-date={endUnix}");
|
||||||
|
|
||||||
if (response == null)
|
if (response == null)
|
||||||
@@ -90,6 +91,25 @@ public class SimpleFinSyncProvider : IAccountSyncProvider
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<SimpleFinAccountSet?> 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<SimpleFinAccountSet>();
|
||||||
|
}
|
||||||
|
|
||||||
private static string GuessAccountType(string name)
|
private static string GuessAccountType(string name)
|
||||||
{
|
{
|
||||||
var lower = name.ToLowerInvariant();
|
var lower = name.ToLowerInvariant();
|
||||||
|
|||||||
Reference in New Issue
Block a user