Skip to content

Commit ec51dd2

Browse files
committed
Credentials invalidation.
1 parent e9144fc commit ec51dd2

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

PowerSync/PowerSync.Common/Client/Sync/Stream/Remote.cs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace PowerSync.Common.Client.Sync.Stream;
66
using System.Text;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using System.Text.RegularExpressions;
910

1011
using Newtonsoft.Json;
1112
using Newtonsoft.Json.Linq;
@@ -29,7 +30,6 @@ public class RequestDetails
2930

3031
public class Remote
3132
{
32-
private static int REFRESH_CREDENTIALS_SAFETY_PERIOD_MS = 30_000;
3333
private readonly HttpClient httpClient;
3434
protected IPowerSyncBackendConnector connector;
3535

@@ -41,18 +41,48 @@ public Remote(IPowerSyncBackendConnector connector)
4141
this.connector = connector;
4242
}
4343

44+
/// <summary>
45+
/// Get credentials currently cached, or fetch new credentials if none are available.
46+
/// These credentials may have expired already.
47+
/// </summary>
4448
public async Task<PowerSyncCredentials?> GetCredentials()
4549
{
46-
if (credentials?.ExpiresAt > DateTime.Now.AddMilliseconds(REFRESH_CREDENTIALS_SAFETY_PERIOD_MS))
50+
if (credentials != null)
4751
{
4852
return credentials;
4953
}
54+
return await PrefetchCredentials();
55+
}
5056

51-
credentials = await connector.FetchCredentials();
52-
57+
/// <summary>
58+
/// Fetch a new set of credentials and cache it.
59+
/// Until this call succeeds, GetCredentials will still return the old credentials.
60+
/// This may be called before the current credentials have expired.
61+
/// </summary>
62+
public async Task<PowerSyncCredentials?> PrefetchCredentials()
63+
{
64+
credentials = await FetchCredentials();
5365
return credentials;
5466
}
5567

68+
/// <summary>
69+
/// Get credentials for PowerSync.
70+
/// This should always fetch a fresh set of credentials - don't use cached values.
71+
/// </summary>
72+
public async Task<PowerSyncCredentials?> FetchCredentials()
73+
{
74+
return await connector.FetchCredentials();
75+
}
76+
77+
/// <summary>
78+
/// Immediately invalidate credentials.
79+
/// This may be called when the current credentials have expired.
80+
/// </summary>
81+
public void InvalidateCredentials()
82+
{
83+
credentials = null;
84+
}
85+
5686
static string GetUserAgent()
5787
{
5888
object[] attributes = Assembly.GetExecutingAssembly()
@@ -76,6 +106,11 @@ public async Task<T> Get<T>(string path, Dictionary<string, string>? headers = n
76106
using var client = new HttpClient();
77107
var response = await client.SendAsync(request);
78108

109+
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
110+
{
111+
InvalidateCredentials();
112+
}
113+
79114
if (!response.IsSuccessStatusCode)
80115
{
81116
var errorMessage = await response.Content.ReadAsStringAsync();
@@ -95,7 +130,12 @@ public async Task<T> Get<T>(string path, Dictionary<string, string>? headers = n
95130
{
96131
throw new HttpRequestException($"HTTP {response.StatusCode}: No content");
97132
}
98-
else
133+
134+
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
135+
{
136+
InvalidateCredentials();
137+
}
138+
99139
if (!response.IsSuccessStatusCode)
100140
{
101141
var errorText = await response.Content.ReadAsStringAsync();

PowerSync/PowerSync.Common/Client/Sync/Stream/StreamingSyncImplementation.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,18 @@ protected async Task<StreamingSyncIterationResult> StreamingSyncIteration(Cancel
539539
{
540540
// Connection would be closed automatically right after this
541541
logger.LogDebug("Token expiring; reconnect");
542+
Options.Remote.InvalidateCredentials();
542543

543544
// For a rare case where the backend connector does not update the token
544545
// (uses the same one), this should have some delay.
545546
//
546547
await DelayRetry();
547548
return new StreamingSyncIterationResult { Retry = true };
549+
} else if (remainingSeconds < 30) {
550+
logger.LogDebug("Token will expire soon; reconnect");
551+
// Pre-emptively refresh the token
552+
Options.Remote.InvalidateCredentials();
553+
return new StreamingSyncIterationResult { Retry = true };
548554
}
549555
TriggerCrudUpload();
550556
}

0 commit comments

Comments
 (0)