2026-06-20 17:49:53 +01:00
|
|
|
using IdentityModel.OidcClient.Browser;
|
|
|
|
|
using PostIt.Services;
|
|
|
|
|
|
|
|
|
|
namespace PostIt.Desktop;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// One-shot platform bootstrap. Called from <c>Program.Main</c> so that
|
|
|
|
|
/// the shared <c>LoginPageViewModel</c> sees a working <c>IBrowser</c>
|
postit: wire BlogApiClient through YavscApiClient and unify auth
BlogApiClient is a thin DTO↔path mapper on top of YavscApiClient:
- No more HttpClient, no more accessToken constructor argument.
- Single responsibility: turn Yavsc.Blogs endpoint paths into typed
BlogPost payloads and back, while YavscApiClient owns auth +
refresh + JSON shape.
- Default path prefix is 'api/blog', overridable for tests.
MainPageViewModel and App.axaml.cs are now free of any direct
OidcClient / IBrowser / BearerToken plumbing. The MainPage receives
a fully-configured BlogApiClient (which holds a YavscApiClient, which
holds a TokenStore) at construction. The duplicate LoginAsync method
on MainPageViewModel is gone; the LoginPage is the single entry point
for the interactive PKCE flow.
PlatformBootstrap.Desktop no longer overrides the redirect URI to
loopback. PostIt runs the postit://callback custom scheme
(SingleInstance hand-off) as the production path on desktop; the
loopback constant stays for tests and for platforms that cannot
register a custom scheme.
Settings.cs: DefaultLoopbackRedirectUri is now documented as a
fallback; DefaultDesktopRedirectUri ('postit://callback') is
introduced as the canonical desktop default.
TokenStore.Load() tolerates an empty file (returns null) so
first-launch races and stubbed test fixtures don't blow up the
constructor.
YavscApiClient:
- HasValidSession is exposed for warm-start UI logic.
- CurrentAccessToken / CurrentIdToken are exposed so the LoginPage
ViewModel can mirror the result onto its observable properties.
- CallAsync<T> is now virtual (and the class is no longer sealed)
to allow stubbing in PostItViewModelTests.
Tests (PostIt.Tests):
- YavscApiClientTests covers the silent refresh path (cache the
token, mark it expired, observe a new Bearer in the API server),
the 401 → refresh → retry path (forceFirstRequest on the stub),
HasValidSession after login, and the throw-when-no-token guard.
- OidcStubAuthority now mints a refresh_token in the token response
so YavscApiClient.RefreshTokenAsync can hit /connect/token in
tests.
- PostItViewModelTests uses a ThrowingYavscApiClient / StubYavscApiClient
pair instead of the old HttpClient injection point, matching the
new constructor shape.
dotnet test: 21/21 green. dotnet build: 0 errors.
2026-06-23 21:25:17 +01:00
|
|
|
/// — the custom-scheme browser that hands the OIDC callback off to the
|
|
|
|
|
/// running instance through the named pipe. Desktop builds do NOT use
|
|
|
|
|
/// a loopback HTTP listener: the <c>postit://</c> scheme is registered
|
|
|
|
|
/// with the OS at install time and the browser is whatever the user
|
|
|
|
|
/// has configured to open it.
|
2026-06-20 17:49:53 +01:00
|
|
|
/// </summary>
|
|
|
|
|
internal static class PlatformBootstrap
|
|
|
|
|
{
|
|
|
|
|
private static int _initialized;
|
|
|
|
|
|
|
|
|
|
internal static void EnsureInitialized()
|
|
|
|
|
{
|
|
|
|
|
if (System.Threading.Interlocked.Exchange(ref _initialized, 1) != 0)
|
|
|
|
|
return;
|
|
|
|
|
|
postit: wire BlogApiClient through YavscApiClient and unify auth
BlogApiClient is a thin DTO↔path mapper on top of YavscApiClient:
- No more HttpClient, no more accessToken constructor argument.
- Single responsibility: turn Yavsc.Blogs endpoint paths into typed
BlogPost payloads and back, while YavscApiClient owns auth +
refresh + JSON shape.
- Default path prefix is 'api/blog', overridable for tests.
MainPageViewModel and App.axaml.cs are now free of any direct
OidcClient / IBrowser / BearerToken plumbing. The MainPage receives
a fully-configured BlogApiClient (which holds a YavscApiClient, which
holds a TokenStore) at construction. The duplicate LoginAsync method
on MainPageViewModel is gone; the LoginPage is the single entry point
for the interactive PKCE flow.
PlatformBootstrap.Desktop no longer overrides the redirect URI to
loopback. PostIt runs the postit://callback custom scheme
(SingleInstance hand-off) as the production path on desktop; the
loopback constant stays for tests and for platforms that cannot
register a custom scheme.
Settings.cs: DefaultLoopbackRedirectUri is now documented as a
fallback; DefaultDesktopRedirectUri ('postit://callback') is
introduced as the canonical desktop default.
TokenStore.Load() tolerates an empty file (returns null) so
first-launch races and stubbed test fixtures don't blow up the
constructor.
YavscApiClient:
- HasValidSession is exposed for warm-start UI logic.
- CurrentAccessToken / CurrentIdToken are exposed so the LoginPage
ViewModel can mirror the result onto its observable properties.
- CallAsync<T> is now virtual (and the class is no longer sealed)
to allow stubbing in PostItViewModelTests.
Tests (PostIt.Tests):
- YavscApiClientTests covers the silent refresh path (cache the
token, mark it expired, observe a new Bearer in the API server),
the 401 → refresh → retry path (forceFirstRequest on the stub),
HasValidSession after login, and the throw-when-no-token guard.
- OidcStubAuthority now mints a refresh_token in the token response
so YavscApiClient.RefreshTokenAsync can hit /connect/token in
tests.
- PostItViewModelTests uses a ThrowingYavscApiClient / StubYavscApiClient
pair instead of the old HttpClient injection point, matching the
new constructor shape.
dotnet test: 21/21 green. dotnet build: 0 errors.
2026-06-23 21:25:17 +01:00
|
|
|
// Use the custom-scheme redirect on Desktop. Loopback is only
|
|
|
|
|
// a fallback for platforms that cannot register postit://
|
|
|
|
|
// (see Settings.DefaultLoopbackRedirectUri for that path).
|
|
|
|
|
Platform.DefaultRedirectUri = Settings.DefaultDesktopRedirectUri;
|
|
|
|
|
Platform.CustomScheme = "postit";
|
2026-06-20 17:49:53 +01:00
|
|
|
}
|
2026-06-22 01:44:24 +01:00
|
|
|
}
|