PostIt.Desktop: wire the loopback browser, parameterise PostIt RedirectUris

The previous commit set Platform.CreateBrowser to null on the desktop
side, so LoginAsync would still fail with 'No browser is available'.
Close that loop with an explicit desktop bootstrap.

PostIt.Desktop/PlatformBootstrap.cs mirrors the Android side: it
populates Platform.DefaultRedirectUri and Platform.CreateBrowser
once at startup. Program.Main calls EnsureInitialized before
BuildAvaloniaApp so the LoginPageViewModel sees a working browser
before any login attempt.

The Yavsc.Org seed now reads Site:ExternalUrl from configuration so
the RedirectUri list for the PostIt client follows the same setting
as the rest of the application (same value used in
Administration/ClientController, AccountController, etc.). Without
this, an embedded 'launch PostIt from a Yavsc.Org page' scenario
would be rejected by IdentityServer (redirect_uri mismatch).

BuildPostItRedirectUris is a small helper that yields the constant
PostItRedirectUris (loopback + Android custom scheme) followed by
Site:ExternalUrl when set. Both SeedNewPostItClient (fresh db) and
MigratePostItClientToPublic (existing db) consume it. The legacy
cleanup block (which used to remove https://yavsc.pschneider.fr/
and yavsc://callback) is dropped: Site:ExternalUrl is now the
canonical way to authorise that path and may legitimately equal
that value.
This commit is contained in:
Paul Schneider 2026-06-20 17:49:53 +01:00
commit 7a0944d0f5
3 changed files with 64 additions and 25 deletions

View file

@ -0,0 +1,24 @@
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>
/// (the loopback listener that captures the OIDC redirect) without
/// referencing any platform-specific API from the shared library.
/// </summary>
internal static class PlatformBootstrap
{
private static int _initialized;
internal static void EnsureInitialized()
{
if (System.Threading.Interlocked.Exchange(ref _initialized, 1) != 0)
return;
Platform.DefaultRedirectUri = Settings.DefaultLoopbackRedirectUri;
Platform.CreateBrowser = () => new LoopbackBrowser();
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using Avalonia;
namespace PostIt.Desktop;
@ -9,8 +9,12 @@ sealed class Program
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
public static void Main(string[] args)
{
PlatformBootstrap.EnsureInitialized();
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
@ -21,4 +25,4 @@ sealed class Program
#endif
.WithInterFont()
.LogToTrace();
}
}