@@ -6,6 +6,7 @@ namespace PowerSync.Common.Client.Sync.Stream;
66using System . Text ;
77using System . Threading ;
88using System . Threading . Tasks ;
9+ using System . Text . RegularExpressions ;
910
1011using Newtonsoft . Json ;
1112using Newtonsoft . Json . Linq ;
@@ -29,7 +30,6 @@ public class RequestDetails
2930
3031public 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 ( ) ;
0 commit comments