diff --git a/docs/concepts/WebAuthenticator.md b/docs/concepts/WebAuthenticator.md index d10415b..9162b8c 100644 --- a/docs/concepts/WebAuthenticator.md +++ b/docs/concepts/WebAuthenticator.md @@ -17,8 +17,29 @@ Next you can make a make a call to authenticate using your default browser: WebAuthenticatorResult result = await WinUIEx.WebAuthenticator.AuthenticateAsync(authorizeUrl, callbackUri); ``` -Your callback uri must use a custom scheme, and you must define this scheme in your application manifest. For example if your callback uri is "myscheme://loggedin", your manifest dialog should look like this: +### Configuration + +Your app must be configured for OAuth with schema activation. The scheme must be the first part of the url, ie if your oauth redirection url starts with for instance `myscheme://signin/`, the scheme would be `myscheme`. Note that http(s) schemes are not supported here. + +#### Packaged Apps + +If your app is packaged, in your app's `Package.appxmanifest` under `Declarations`, add a Protocol declaration and add the scheme you registered for your application's oauth redirect url under "Name". +For example if your callback uri is "myscheme://loggedin", your manifest dialog should look like this: ![image](https://user-images.githubusercontent.com/1378165/166501267-1da07930-ab4d-431e-87cf-a7b183cc3c87.png) +#### Unpackaged Apps + +If your app is unpackaged, instead of relying on the app manifest to handle this for you, make sure you register the application for protocol activation. For example: +``` cs +try +{ + Microsoft.Windows.AppLifecycle.ActivationRegistrationManager.RegisterForProtocolActivation("myscheme", "Assets\\Square150x150Logo.scale-100", "My App Name", null); + var result = await WebAuthenticator.AuthenticateAsync(authorizeUri, callbackUri, cancellationToken); +} +finally +{ + Microsoft.Windows.AppLifecycle.ActivationRegistrationManager.UnregisterForProtocolActivation("myscheme", null); +} +``` \ No newline at end of file diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 9a1a617..f4a053d 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -17,8 +17,8 @@ Morten Nielsen - https://xaml.dev Morten Nielsen - https://xaml.dev logo.png - 2.5.0 - + 2.5.1 + 2.5.0 diff --git a/src/WinUIEx/WebAuthenticator.cs b/src/WinUIEx/WebAuthenticator.cs index 4243499..48798e5 100644 --- a/src/WinUIEx/WebAuthenticator.cs +++ b/src/WinUIEx/WebAuthenticator.cs @@ -19,9 +19,30 @@ namespace WinUIEx /// /// /// - /// Your app must be configured for OAuth. In you app package's Package.appxmanifest under Declarations, add a + /// Your app must be configured for OAuth with schema activation. The scheme must be the first part of the url, ie if your + /// oauth redirection url starts with for instance myappscheme://signin/, the scheme would be myappscheme. + /// Note that http(s) schemes are not supported here. + /// + /// + /// If your app is packaged, in your app's Package.appxmanifest under Declarations, add a /// Protocol declaration and add the scheme you registered for your application's oauth redirect url under "Name". /// + /// + /// If your app is unpackaged, make sure you register the application for protocol activation. + /// + /// + /// + /// try + /// { + /// Microsoft.Windows.AppLifecycle.ActivationRegistrationManager.RegisterForProtocolActivation("myappscheme", "Assets\\Square150x150Logo.scale-100", "My App Name", null); + /// var result = await WebAuthenticator.AuthenticateAsync(authorizeUri, callbackUri, cancellationToken); + /// } + /// finally + /// { + /// Microsoft.Windows.AppLifecycle.ActivationRegistrationManager.UnregisterForProtocolActivation("myappscheme", null); + /// } + /// + /// /// public sealed class WebAuthenticator { @@ -218,11 +239,20 @@ private async Task Authenticate(Uri authorizeUri, Uri ca } if (!Helpers.IsAppPackaged) { - throw new InvalidOperationException("The WebAuthenticator requires a packaged app with an AppxManifest"); + if(callbackUri.Scheme == "http" || callbackUri.Scheme == "https") + throw new InvalidOperationException($"{callbackUri.Scheme}:// schemes are not allowed for callbackUri. Use a custom scheme like 'myapp' instead."); + var value = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(callbackUri.Scheme); + if(value is null || value.GetValue("URL Protocol") is null) + { + throw new InvalidOperationException($"The URI Scheme '{callbackUri.Scheme}' is not registered. Call ActivationRegistrationManager.RegisterForProtocolActivation to register protocol activation."); + } } - if (!IsUriProtocolDeclared(callbackUri.Scheme)) + else { - throw new InvalidOperationException($"The URI Scheme {callbackUri.Scheme} is not declared in AppxManifest.xml"); + if (!IsUriProtocolDeclared(callbackUri.Scheme)) + { + throw new InvalidOperationException($"The URI Scheme {callbackUri.Scheme} is not declared in AppxManifest.xml"); + } } var g = Guid.NewGuid(); var taskId = g.ToString(); diff --git a/src/WinUIEx/WinUIEx.csproj b/src/WinUIEx/WinUIEx.csproj index 51350d9..80180ca 100644 --- a/src/WinUIEx/WinUIEx.csproj +++ b/src/WinUIEx/WinUIEx.csproj @@ -25,7 +25,7 @@ WinUIEx WinUI Extensions - - Added `SimpleSplashScreen` for display application launch splash screens + - WebAuthenticator: Added support for unpackaged apps. diff --git a/src/WinUIExSample/App.xaml.cs b/src/WinUIExSample/App.xaml.cs index db5a56b..0212ad8 100644 --- a/src/WinUIExSample/App.xaml.cs +++ b/src/WinUIExSample/App.xaml.cs @@ -29,16 +29,12 @@ public App() fss = SimpleSplashScreen.ShowDefaultSplashScreen(); #endif this.InitializeComponent(); - int length = 0; - var sb = new System.Text.StringBuilder(0); - int result = GetCurrentPackageFullName(ref length, sb); - if(result == 15700L) - { - // Not a packaged app. Configure file-based persistence instead - WinUIEx.WindowManager.PersistenceStorage = new FilePersistence("WinUIExPersistence.json"); - } - +#if UNPACKAGED + // Use file-based persistence since we can't rely on default storage for window persistence when unpackaged + WinUIEx.WindowManager.PersistenceStorage = new FilePersistence("WinUIExPersistence.json"); +#endif } + internal SimpleSplashScreen fss { get; set; } /// /// Invoked when the application is launched normally by the end user. Other entry points @@ -67,9 +63,7 @@ private void Splash_Activated(object sender, WindowActivatedEventArgs args) public WindowEx MainWindow => m_window; - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, System.Text.StringBuilder packageFullName); - +#if UNPACKAGED private class FilePersistence : IDictionary { private readonly Dictionary _data = new Dictionary(); @@ -143,6 +137,7 @@ public void Clear() IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); // TODO } +#endif } #if DISABLE_XAML_GENERATED_MAIN @@ -156,7 +151,11 @@ static void Main(string[] args) { if (WebAuthenticator.CheckOAuthRedirectionActivation(true)) return; +#if UNPACKAGED + var fss = SimpleSplashScreen.ShowSplashScreenImage("Assets\\SplashScreen.scale-100.png"); +#else var fss = SimpleSplashScreen.ShowDefaultSplashScreen(); +#endif global::WinRT.ComWrappersSupport.InitializeComWrappers(); global::Microsoft.UI.Xaml.Application.Start((p) => { var context = new global::Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext(global::Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread()); @@ -166,4 +165,5 @@ static void Main(string[] args) } } #endif + } } diff --git a/src/WinUIExSample/Pages/OAuth.xaml.cs b/src/WinUIExSample/Pages/OAuth.xaml.cs index 725c0c9..3e28de5 100644 --- a/src/WinUIExSample/Pages/OAuth.xaml.cs +++ b/src/WinUIExSample/Pages/OAuth.xaml.cs @@ -65,6 +65,10 @@ private async void DoOAuth(string responseType) OAuthWindow.Visibility = Visibility.Visible; try { +#if UNPACKAGED + // Packaged app uses appxmanifest for protocol activation. Unpackaged apps must manually register + Microsoft.Windows.AppLifecycle.ActivationRegistrationManager.RegisterForProtocolActivation("winuiex", "Assets\\Square150x150Logo.scale-100", "WinUI EX", null); +#endif var result = await WebAuthenticator.AuthenticateAsync(new Uri(authorizeUri), new Uri(callbackUri), oauthCancellationSource.Token); MainWindow.BringToFront(); OAuthWindow.Visibility = Visibility.Collapsed; @@ -75,6 +79,12 @@ private async void DoOAuth(string responseType) catch (TaskCanceledException) { Result.Text = "Sign in cancelled"; } + finally + { +#if UNPACKAGED + Microsoft.Windows.AppLifecycle.ActivationRegistrationManager.UnregisterForProtocolActivation("winuiex", null); +#endif + } } private void OAuthCancel_Click(object sender, RoutedEventArgs e) diff --git a/src/WinUIExSample/WinUIExSample.csproj b/src/WinUIExSample/WinUIExSample.csproj index 258cacb..3a674d9 100644 --- a/src/WinUIExSample/WinUIExSample.csproj +++ b/src/WinUIExSample/WinUIExSample.csproj @@ -18,6 +18,7 @@ 10.0.22621.38 DISABLE_XAML_GENERATED_MAIN;$(DefineConstants) + UNPACKAGED;$(DefineConstants) true