PostIt: switch OIDC client from confidential (client_secret) to public (PKCE/JWT)
PostIt is a desktop/mobile app talking to Yavsc.Org (https://yavsc.pschneider.fr) as an OIDC identity provider. The previous grant used the client_credentials flow with a client_secret embedded in postit-settings.json: this was both insecure (secret travels with the binary) and inappropriate for an interactive app (token had no user identity, so the API could not scope or audit). The new flow is Authorization Code + PKCE: * PostIt client (Settings/AuthenticationSettings.cs): the ClientSecret property is removed; GetOidcClientOptions now drops the secret and accepts an optional IBrowser supplied per-platform. * Settings.cs: new AndroidRedirectUri constant ('android://postit-signin') that the Android app uses; RedirectUri is no longer hard-coded in MainViewModel. * MainViewModel.cs: the manual discovery + client_credentials POST is replaced with OidcClient.LoginAsync (Authorization Code + PKCE). * Settings sample: Authority points at the real Yavsc.Org OP, not at a non-existent Keycloak-style realm path. * Yavsc.Org/Extensions/HostingExtensions.cs: the 'postit' client seed is now idempotent (MigratePostItClientToPublic) and detects legacy state on existing ConfigurationDb rows - flips RequireClientSecret=false, RequirePkce=true, drops any ClientSecret row, and replaces the legacy RedirectUris (https://yavsc.pschneider.fr/, yavsc://callback) with the current set (http://127.0.0.1:7890/, android://postit-signin). PostIt.Android: * MainActivity: explicit Name attribute so the activity alias can target a stable component; LaunchMode.SingleTask so the existing instance receives the deep-link Intent; OnNewIntent forwards the callback URI through AndroidOidcCallbackSink. * AndroidManifest.xml: activity-alias PostIt.Android.OidcCallbackActivity exposing scheme=android host=postit-signin to Android, so the OP redirect lands back in the running PostIt instance. The IdentityModel.OidcClient.Browser.SystemBrowser package and a thin AndroidSystemBrowser implementation are added in a follow-up so OidcClient.LoginAsync can actually drive Chrome Custom Tabs and consume AndroidOidcCallbackSink.
This commit is contained in:
parent
2fd799c09f
commit
512a0ef06f
9 changed files with 324 additions and 116 deletions
|
|
@ -1,16 +1,51 @@
|
|||
using Android.App;
|
||||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.Content;
|
||||
using Avalonia;
|
||||
using Avalonia.Android;
|
||||
|
||||
namespace PostIt.Android;
|
||||
|
||||
[Activity(
|
||||
Name = "PostIt.Android.PostItMainActivity",
|
||||
Label = "PostIt.Android",
|
||||
Theme = "@style/MyTheme.NoActionBar",
|
||||
Icon = "@drawable/icon",
|
||||
MainLauncher = true,
|
||||
LaunchMode = LaunchMode.SingleTask,
|
||||
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
|
||||
public class MainActivity : AvaloniaMainActivity
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Receives the deep-link Intent fired by the system browser after the
|
||||
/// user completes the OIDC login on https://yavsc.pschneider.fr. The
|
||||
/// Intent URI has the shape <c>android://postit-signin?code=...&state=...</c>.
|
||||
///
|
||||
/// IdentityModel.OidcClient.Browser.SystemBrowser is set up to await this
|
||||
/// callback via a TaskCompletionSource; expose the received Intent here
|
||||
/// through a static sink so the browser can resolve the pending login.
|
||||
/// </summary>
|
||||
protected override void OnNewIntent(Intent? intent)
|
||||
{
|
||||
base.OnNewIntent(intent);
|
||||
if (intent is not null) AndroidOidcCallbackSink.Handle(intent);
|
||||
}
|
||||
|
||||
internal static class AndroidOidcCallbackSink
|
||||
{
|
||||
private static System.Threading.Tasks.TaskCompletionSource<string>? _pending;
|
||||
|
||||
public static System.Threading.Tasks.Task<string> AwaitNextCallbackAsync()
|
||||
{
|
||||
_pending = new System.Threading.Tasks.TaskCompletionSource<string>(
|
||||
System.Threading.Tasks.TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
return _pending.Task;
|
||||
}
|
||||
|
||||
public static void Handle(Intent intent)
|
||||
{
|
||||
var tcs = System.Threading.Interlocked.Exchange(ref _pending, null);
|
||||
tcs?.TrySetResult(intent?.Data?.ToString() ?? string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue