From d819e8cde565207f7af09fb7a1a8b8d0e4f0226b Mon Sep 17 00:00:00 2001 From: Frederik Date: Thu, 26 Oct 2023 14:45:43 +0200 Subject: [PATCH] Add comments --- nuget/Auth0.OidcClient.MAUI.nuspec | 2 +- .../Activator.cs | 53 ++++++++------ .../AppActivationArguments.cs | 15 ++++ .../AppInstanceProxy.cs | 70 ++++++++++++++++--- .../Helpers.cs | 20 ++++++ .../RedirectionContext.cs | 17 +++++ .../RedirectionContextManager.cs | 24 +++++-- .../StateModifier.cs | 44 ++++++++---- .../WebAuthenticator.cs | 23 ++---- .../WebAuthenticatorResult.cs | 11 +-- .../StateModifiedTests.cs | 6 +- .../WebAuthenticatorTests.cs | 59 +++++++++++----- 12 files changed, 242 insertions(+), 102 deletions(-) create mode 100644 src/Auth0.OidcClient.MAUI.Platforms.Windows/AppActivationArguments.cs diff --git a/nuget/Auth0.OidcClient.MAUI.nuspec b/nuget/Auth0.OidcClient.MAUI.nuspec index 746b80bc..312aecb0 100644 --- a/nuget/Auth0.OidcClient.MAUI.nuspec +++ b/nuget/Auth0.OidcClient.MAUI.nuspec @@ -2,7 +2,7 @@ Auth0.OidcClient.MAUI - 1.0.0-beta.1 + 1.0.0-beta.0 Auth0 Auth0 Apache-2.0 diff --git a/src/Auth0.OidcClient.MAUI.Platforms.Windows/Activator.cs b/src/Auth0.OidcClient.MAUI.Platforms.Windows/Activator.cs index 5e835c95..05495923 100644 --- a/src/Auth0.OidcClient.MAUI.Platforms.Windows/Activator.cs +++ b/src/Auth0.OidcClient.MAUI.Platforms.Windows/Activator.cs @@ -3,47 +3,54 @@ namespace Auth0.OidcClient.Platforms.Windows { - public sealed class Activator + public interface IActivator { - internal static bool RedirectActivationCheck; + bool RedirectActivationChecked { get; } + bool CheckRedirectionActivation(); + } + + /// + /// Activator class used to enable protocol activation check and redirects activation to the correct application instance + /// + public sealed class Activator : IActivator + { + private readonly IAppInstanceProxy _appInstanceProxy; + + public static readonly Activator Default = new Activator(new AppInstanceProxy()); + + internal Activator(IAppInstanceProxy appInstanceProxy) + { + _appInstanceProxy = appInstanceProxy; + } /// - /// Performs an protocol activation check and redirects activation to the correct application instance. + /// Boolean indication the redirect activation was checked /// - public static bool CheckRedirectionActivation() + public bool RedirectActivationChecked { get; internal set; } + + /// + /// Performs a protocol activation check and redirects activation to the correct application instance. + /// + public bool CheckRedirectionActivation() { - var activatedEventArgs = AppInstance.GetCurrent()?.GetActivatedEventArgs(); + var activatedEventArgs = _appInstanceProxy.GetCurrentActivatedEventArgs(); - RedirectActivationCheck = true; + RedirectActivationChecked = true; if (activatedEventArgs is null || activatedEventArgs.Kind != ExtendedActivationKind.Protocol || activatedEventArgs.Data is not IProtocolActivatedEventArgs protocolArgs) { return false; } - var ctx = RedirectionContextManager.GetRedirectionContext(activatedEventArgs.Data as IProtocolActivatedEventArgs); + var ctx = RedirectionContextManager.GetRedirectionContext(protocolArgs); if (ctx is not null && ctx.AppInstanceKey is not null && ctx.TaskId is not null) { - var instance = AppInstance.GetInstances().FirstOrDefault(i => i.Key == ctx.AppInstanceKey); - - if (instance is not null && !instance.IsCurrent) - { - instance.RedirectActivationToAsync(activatedEventArgs).AsTask().Wait(); - - System.Diagnostics.Process.GetCurrentProcess().Kill(); - - return true; - } + return _appInstanceProxy.RedirectActivationToAsync(ctx.AppInstanceKey, activatedEventArgs); } else { - var instance = AppInstance.GetCurrent(); - - if (string.IsNullOrEmpty(instance.Key)) - { - AppInstance.FindOrRegisterForKey(Guid.NewGuid().ToString()); - } + _appInstanceProxy.FindOrRegisterForKey(); } return false; } diff --git a/src/Auth0.OidcClient.MAUI.Platforms.Windows/AppActivationArguments.cs b/src/Auth0.OidcClient.MAUI.Platforms.Windows/AppActivationArguments.cs new file mode 100644 index 00000000..936d4aca --- /dev/null +++ b/src/Auth0.OidcClient.MAUI.Platforms.Windows/AppActivationArguments.cs @@ -0,0 +1,15 @@ +using Microsoft.Windows.AppLifecycle; + +namespace Auth0.OidcClient.Platforms.Windows; + +internal interface IAppActivationArguments +{ + ExtendedActivationKind Kind { get; set; } + object Data { get; set; } +} + +internal class AppActivationArguments : IAppActivationArguments +{ + public ExtendedActivationKind Kind { get; set; } + public object Data { get; set; } +} \ No newline at end of file diff --git a/src/Auth0.OidcClient.MAUI.Platforms.Windows/AppInstanceProxy.cs b/src/Auth0.OidcClient.MAUI.Platforms.Windows/AppInstanceProxy.cs index ac09accb..075bd000 100644 --- a/src/Auth0.OidcClient.MAUI.Platforms.Windows/AppInstanceProxy.cs +++ b/src/Auth0.OidcClient.MAUI.Platforms.Windows/AppInstanceProxy.cs @@ -7,18 +7,12 @@ internal interface IAppInstanceProxy { event EventHandler Activated; string GetCurrentAppKey(); -} + Microsoft.Windows.AppLifecycle.AppActivationArguments GetCurrentActivatedEventArgs(); -internal interface IAppActivationArguments -{ - ExtendedActivationKind Kind { get; set; } - object Data { get; set; } -} + bool RedirectActivationToAsync(string key, + Microsoft.Windows.AppLifecycle.AppActivationArguments activatedEventArgs); -internal class AppActivationArguments : IAppActivationArguments -{ - public ExtendedActivationKind Kind { get; set; } - public object Data { get; set; } + void FindOrRegisterForKey(); } /// @@ -43,8 +37,64 @@ protected virtual void OnActivated(object? sender, Microsoft.Windows.AppLifecycl }); } + /// + /// Get the current application key. + /// + /// + /// Proxy call to AppInstance.GetCurrent().Key. + /// Used because AppInstance is complicated to use in tests. + /// + /// The key for the current application. public virtual string GetCurrentAppKey() { return AppInstance.GetCurrent().Key; } + + /// + /// Get the current application + /// + /// + /// Proxy call to AppInstance.GetCurrent().GetActivatedEventArgs(). + /// Used because AppInstance is complicated to use in tests. + /// + /// Null if no current application instance is found, or the corresponding . + public virtual Microsoft.Windows.AppLifecycle.AppActivationArguments GetCurrentActivatedEventArgs() + { + return AppInstance.GetCurrent()?.GetActivatedEventArgs(); + } + + /// + /// Redirect the activation to the correct application instance and kill the current process. + /// + /// Key of the application to activated + /// to pass to the application. + /// Boolean indicating an application instance was activated. + public virtual bool RedirectActivationToAsync(string key, Microsoft.Windows.AppLifecycle.AppActivationArguments activatedEventArgs) + { + var instance = AppInstance.GetInstances().FirstOrDefault(i => i.Key == key); + + if (instance is not null && !instance.IsCurrent) + { + instance.RedirectActivationToAsync(activatedEventArgs).AsTask().Wait(); + + System.Diagnostics.Process.GetCurrentProcess().Kill(); + + return true; + } + + return false; + } + + /// + /// Registers the current application using a new key. + /// + public virtual void FindOrRegisterForKey() + { + var instance = AppInstance.GetCurrent(); + + if (string.IsNullOrEmpty(instance.Key)) + { + AppInstance.FindOrRegisterForKey(Guid.NewGuid().ToString()); + } + } } \ No newline at end of file diff --git a/src/Auth0.OidcClient.MAUI.Platforms.Windows/Helpers.cs b/src/Auth0.OidcClient.MAUI.Platforms.Windows/Helpers.cs index ce33808f..8e7aa153 100644 --- a/src/Auth0.OidcClient.MAUI.Platforms.Windows/Helpers.cs +++ b/src/Auth0.OidcClient.MAUI.Platforms.Windows/Helpers.cs @@ -12,6 +12,7 @@ internal interface IHelpers bool IsUriProtocolDeclared(string scheme); void OpenBrowser(Uri uri); } + internal class Helpers : IHelpers { #pragma warning disable SA1203 // Constants should appear before fields @@ -21,6 +22,13 @@ internal class Helpers : IHelpers [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, System.Text.StringBuilder packageFullName); + /// + /// Helper property to verify the application is packaged. + /// + /// + /// Original source: https://github.com/dotMorten/WinUIEx + /// + /// A boolean indicate whether or not the app is packaged. public bool IsAppPackaged { get @@ -40,6 +48,14 @@ public bool IsAppPackaged } } + /// + /// Helper method to verify the scheme is defined as a protocol in the AppxManifest.xml files + /// + /// + /// Original source: https://github.com/dotMorten/WinUIEx + /// + /// The scheme expected to be declared. + /// A boolean indicate whether or not the scheme is declared as an Uri protocol. public bool IsUriProtocolDeclared(string scheme) { if (global::Windows.ApplicationModel.Package.Current is null) @@ -57,6 +73,10 @@ public bool IsUriProtocolDeclared(string scheme) return decl != null && decl.Any(); } + /// + /// Helper method to open the browser through the url.dll. + /// + /// The Uri to open public void OpenBrowser(Uri uri) { var process = new System.Diagnostics.Process(); diff --git a/src/Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContext.cs b/src/Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContext.cs index b26e3947..d930075d 100644 --- a/src/Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContext.cs +++ b/src/Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContext.cs @@ -5,9 +5,21 @@ namespace Auth0.OidcClient.Platforms.Windows { internal class RedirectionContext { + /// + /// The id of the task associated with the current redirect. + /// internal string TaskId { get; set; } + + /// + /// The key of the application associated with the current redirect. + /// internal string AppInstanceKey { get; set; } + /// + /// Generate a new based on the application instance. + /// + /// The current application instance. + /// The newly created internal static RedirectionContext New(IAppInstanceProxy appInstanceProxy) { return new RedirectionContext @@ -17,6 +29,11 @@ internal static RedirectionContext New(IAppInstanceProxy appInstanceProxy) }; } + /// + /// Converts the to a for serialization. + /// + /// A holding an optional state property to incorporate in the . + /// internal JsonObject ToJsonObject(NameValueCollection query) { var jsonObject = new JsonObject diff --git a/src/Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContextManager.cs b/src/Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContextManager.cs index 4d78976e..17fc0e30 100644 --- a/src/Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContextManager.cs +++ b/src/Auth0.OidcClient.MAUI.Platforms.Windows/RedirectionContextManager.cs @@ -7,10 +7,15 @@ namespace Auth0.OidcClient.Platforms.Windows { internal class RedirectionContextManager { + /// + /// Gets the from the provided + /// + /// The event arguments associated with the corresponding protocol activation. + /// The newly created . internal static RedirectionContext? GetRedirectionContext(IProtocolActivatedEventArgs protocolArgs) { - var vals = System.Web.HttpUtility.ParseQueryString(protocolArgs.Uri.Query); - var state = vals["state"]; + var query = System.Web.HttpUtility.ParseQueryString(protocolArgs.Uri.Query); + var state = query["state"]; JsonObject jsonObject = null; if (!string.IsNullOrEmpty(state)) @@ -26,13 +31,18 @@ internal class RedirectionContextManager TaskId = TryGetJsonValue(jsonObject, "taskId") }; } - else - { - return null; - } + + return null; } - private static string? TryGetJsonValue(JsonObject jsonObject, string key) + + /// + /// Helper method to try and get a value from a . + /// + /// The corresponding . + /// The key for the value to be retrieve from the + /// The value from the provided key, or null if not found. + private static string TryGetJsonValue(JsonObject jsonObject, string key) { if (jsonObject.ContainsKey(key) && jsonObject[key] is JsonValue jValue && jValue.TryGetValue(out string value)) { diff --git a/src/Auth0.OidcClient.MAUI.Platforms.Windows/StateModifier.cs b/src/Auth0.OidcClient.MAUI.Platforms.Windows/StateModifier.cs index eeb5b8fc..0966cace 100644 --- a/src/Auth0.OidcClient.MAUI.Platforms.Windows/StateModifier.cs +++ b/src/Auth0.OidcClient.MAUI.Platforms.Windows/StateModifier.cs @@ -7,7 +7,7 @@ internal class StateModifier /// /// Takes the state query param, and moves it to be a query param on the URL passed to returnTo /// - /// + /// The Uri instance that contains both a state and returnTo query parameter. /// internal static Uri MoveStateToReturnTo(Uri uri) { @@ -17,7 +17,7 @@ internal static Uri MoveStateToReturnTo(Uri uri) // The original returnTo as configured externally var returnTo = query["returnTo"]; - UriBuilder returnToBuilder = new UriBuilder(returnTo); + UriBuilder returnToBuilder = new UriBuilder(returnTo!); // Get the original returnTo querystring params, so we can append state to it var returnToQuery = System.Web.HttpUtility.ParseQueryString(new Uri(returnTo).Query); @@ -25,7 +25,7 @@ internal static Uri MoveStateToReturnTo(Uri uri) // We need to escape it for it to be accepted returnToQuery["state"] = state; // Set the query again on the returnTo url - returnToBuilder.Query = returnToQuery.ToString(); + returnToBuilder.Query = returnToQuery.ToString() ?? string.Empty; // Update returnTo in the original query so that it now includes state query["returnTo"] = returnToBuilder.Uri.ToString(); @@ -34,29 +34,45 @@ internal static Uri MoveStateToReturnTo(Uri uri) UriBuilder uriBuilder = new UriBuilder(uri); // Set the query again on the logout url - uriBuilder.Query = query.ToString(); + uriBuilder.Query = query.ToString() ?? string.Empty; // Return the Uri so it can be used internally by WinUIEx to start the process and open the browser return uriBuilder.Uri; } - internal static Uri ResetRawState(Uri uri) + /// + /// Wraps the state query parameter with the redirection context. + /// + /// The uri containing a state parameter. + /// The used to wrap the state query parameter. + /// + internal static Uri WrapStateWithRedirectionContext(Uri uri, RedirectionContext redirectContext) { var query = System.Web.HttpUtility.ParseQueryString(uri.Query); + var redirectContextJson = redirectContext.ToJsonObject(query); - var state = Helpers.Decode(query["state"]); + query["state"] = Helpers.Encode(redirectContextJson.ToJsonString()); - JsonObject jsonObject; - try + UriBuilder authorizeUriBuilder = new UriBuilder(uri) { - jsonObject = JsonNode.Parse(state ?? "{}") as JsonObject; + Query = query.ToString() ?? string.Empty + }; - } - catch (Exception ex) - { + return authorizeUriBuilder.Uri; + } - jsonObject = JsonNode.Parse(Uri.UnescapeDataString(state)) as JsonObject; - } + /// + /// Unwraps the state and removes the redirection context from it. + /// + /// The uri containing a wrapped state query parameter. + /// The uri contained an unwrapped state query parameter. + internal static Uri UnwrapRedirectionContextFromState(Uri uri) + { + var query = System.Web.HttpUtility.ParseQueryString(uri.Query); + + var state = Helpers.Decode(query["state"]); + + JsonObject jsonObject = JsonNode.Parse(state ?? "{}") as JsonObject; var originalState = jsonObject["state"]; diff --git a/src/Auth0.OidcClient.MAUI.Platforms.Windows/WebAuthenticator.cs b/src/Auth0.OidcClient.MAUI.Platforms.Windows/WebAuthenticator.cs index 8018d7b5..279f1e6e 100644 --- a/src/Auth0.OidcClient.MAUI.Platforms.Windows/WebAuthenticator.cs +++ b/src/Auth0.OidcClient.MAUI.Platforms.Windows/WebAuthenticator.cs @@ -1,5 +1,4 @@ using Microsoft.Windows.AppLifecycle; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Windows.ApplicationModel.Activation; @@ -11,14 +10,16 @@ public sealed class WebAuthenticator private readonly IHelpers _helpers; private readonly IAppInstanceProxy _appInstanceProxy; private readonly ITasksManager _tasksManager; + private readonly IActivator _activator; - public static readonly WebAuthenticator Default = new WebAuthenticator(new AppInstanceProxy(), new Helpers(), TasksManager.Default); + public static readonly WebAuthenticator Default = new WebAuthenticator(new AppInstanceProxy(), new Helpers(), TasksManager.Default, Activator.Default); - internal WebAuthenticator(IAppInstanceProxy appInstanceProxy, IHelpers helpers, ITasksManager tasksManager) + internal WebAuthenticator(IAppInstanceProxy appInstanceProxy, IHelpers helpers, ITasksManager tasksManager, IActivator activator) { _helpers = helpers; _appInstanceProxy = appInstanceProxy; _tasksManager = tasksManager; + _activator = activator; appInstanceProxy.Activated += CurrentAppInstance_Activated; } @@ -54,7 +55,7 @@ private void CurrentAppInstance_Activated(object? sender, IAppActivationArgument /// public async Task AuthenticateAsync(Uri authorizeUri, Uri callbackUri, CancellationToken cancellationToken = default(CancellationToken)) { - if (!Activator.RedirectActivationCheck) + if (!_activator.RedirectActivationChecked) { throw new InvalidOperationException("The redirection check on app activation was not detected. Please make sure a call to Activator.CheckRedirectionActivation was made during App creation."); } @@ -69,17 +70,7 @@ private void CurrentAppInstance_Activated(object? sender, IAppActivationArgument var redirectContext = RedirectionContext.New(_appInstanceProxy); - var query = System.Web.HttpUtility.ParseQueryString(authorizeUri.Query); - var redirectContextJson = redirectContext.ToJsonObject(query); - - query["state"] = Helpers.Encode(redirectContextJson.ToJsonString()); - - // Update the AuthorizeUri's Query parameter. - UriBuilder authorizeUriBuilder = new UriBuilder(authorizeUri) - { - Query = query.ToString() ?? string.Empty - }; - authorizeUri = authorizeUriBuilder.Uri; + authorizeUri = StateModifier.WrapStateWithRedirectionContext(authorizeUri, redirectContext); var tcs = new TaskCompletionSource(); @@ -102,7 +93,7 @@ private void CurrentAppInstance_Activated(object? sender, IAppActivationArgument _tasksManager.Add(redirectContext.TaskId, tcs); var uri = await tcs.Task.ConfigureAwait(false); - return new WebAuthenticatorResult(StateModifier.ResetRawState(uri)); + return new WebAuthenticatorResult(StateModifier.UnwrapRedirectionContextFromState(uri)); } } } \ No newline at end of file diff --git a/src/Auth0.OidcClient.MAUI.Platforms.Windows/WebAuthenticatorResult.cs b/src/Auth0.OidcClient.MAUI.Platforms.Windows/WebAuthenticatorResult.cs index 49d33055..af5714f4 100644 --- a/src/Auth0.OidcClient.MAUI.Platforms.Windows/WebAuthenticatorResult.cs +++ b/src/Auth0.OidcClient.MAUI.Platforms.Windows/WebAuthenticatorResult.cs @@ -11,12 +11,7 @@ public class WebAuthenticatorResult /// /// Initializes a new instance of the class by parsing a URI's query string parameters. /// - /// - /// If the responseDecoder is non-null, then it is used to decode the fragment or query string - /// returned by the authorization service. Otherwise, a default response decoder is used. - /// /// The callback uri that was used to end the authentication sequence. - /// The decoder that can be used to decode the callback uri. public WebAuthenticatorResult(Uri uri) { CallbackUri = uri; @@ -28,12 +23,8 @@ public WebAuthenticatorResult(Uri uri) } /// - /// The uri that was used to call back with the access token. + /// The uri that was used to call back. /// - /// - /// The value of the callback URI, including the fragment or query string bearing - /// the access token and associated information. - /// public Uri CallbackUri { get; } /// diff --git a/test/Auth0.OidcClient.MAUI.Platforms.Windows.UnitTests/StateModifiedTests.cs b/test/Auth0.OidcClient.MAUI.Platforms.Windows.UnitTests/StateModifiedTests.cs index d2ff8b5c..06b2a518 100644 --- a/test/Auth0.OidcClient.MAUI.Platforms.Windows.UnitTests/StateModifiedTests.cs +++ b/test/Auth0.OidcClient.MAUI.Platforms.Windows.UnitTests/StateModifiedTests.cs @@ -44,7 +44,7 @@ public void Should_reset_raw_state() - var newUri = StateModifier.ResetRawState(originalUriBuilder.Uri); + var newUri = StateModifier.UnwrapRedirectionContextFromState(originalUriBuilder.Uri); var newQuery = System.Web.HttpUtility.ParseQueryString(newUri.Query); var newState = newQuery["state"]; @@ -74,7 +74,7 @@ public void Should_reset_raw_state_when_escaped() - var newUri = StateModifier.ResetRawState(originalUriBuilder.Uri); + var newUri = StateModifier.UnwrapRedirectionContextFromState(originalUriBuilder.Uri); var newQuery = System.Web.HttpUtility.ParseQueryString(newUri.Query); var newState = newQuery["state"]; @@ -103,7 +103,7 @@ public void Should_remove_state_when_no_original_state() - var newUri = StateModifier.ResetRawState(originalUriBuilder.Uri); + var newUri = StateModifier.UnwrapRedirectionContextFromState(originalUriBuilder.Uri); var newQuery = System.Web.HttpUtility.ParseQueryString(newUri.Query); var newState = newQuery["state"]; diff --git a/test/Auth0.OidcClient.MAUI.Platforms.Windows.UnitTests/WebAuthenticatorTests.cs b/test/Auth0.OidcClient.MAUI.Platforms.Windows.UnitTests/WebAuthenticatorTests.cs index fd577c95..992e20ef 100644 --- a/test/Auth0.OidcClient.MAUI.Platforms.Windows.UnitTests/WebAuthenticatorTests.cs +++ b/test/Auth0.OidcClient.MAUI.Platforms.Windows.UnitTests/WebAuthenticatorTests.cs @@ -24,11 +24,12 @@ public async void Should_Throw_When_Not_Checked_For_Redirection_Activation() var mockAppInstance = new Mock(); var mockHelpers = new Mock(); var mockTasksManager = new Mock(); + var mockActivator = new Mock(); - Activator.RedirectActivationCheck = false; + mockActivator.SetupGet(h => h.RedirectActivationChecked).Returns(false); var exception = await Assert.ThrowsAsync(() => - new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object).AuthenticateAsync(new Uri("http://www.idp.com"), new Uri("nyapp://callback"))); + new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object, mockActivator.Object).AuthenticateAsync(new Uri("http://www.idp.com"), new Uri("nyapp://callback"))); Assert.Equal("The redirection check on app activation was not detected. Please make sure a call to Activator.CheckRedirectionActivation was made during App creation.", exception.Message); } @@ -39,11 +40,12 @@ public async void Should_Not_Throw_When_Checked_For_Redirection_Activation() var mockAppInstance = new Mock(); var mockHelpers = new Mock(); var mockTasksManager = new Mock(); + var mockActivator = new Mock(); - Activator.RedirectActivationCheck = true; + mockActivator.SetupGet(h => h.RedirectActivationChecked).Returns(true); var exception = await Assert.ThrowsAsync(() => - new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object).AuthenticateAsync(new Uri("http://www.idp.com"), new Uri("nyapp://callback"))); + new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object, mockActivator.Object).AuthenticateAsync(new Uri("http://www.idp.com"), new Uri("nyapp://callback"))); Assert.NotEqual("The redirection check on app activation was not detected. Please make sure a call to Activator.CheckRedirectionActivation was made during App creation.", exception.Message); } @@ -54,13 +56,14 @@ public async void Should_Throw_When_App_Not_Packaged() var mockAppInstance = new Mock(); var mockHelpers = new Mock(); var mockTasksManager = new Mock(); + var mockActivator = new Mock(); - Activator.RedirectActivationCheck = true; + mockActivator.SetupGet(h => h.RedirectActivationChecked).Returns(true); mockHelpers.SetupGet(h => h.IsAppPackaged).Returns(false); var exception = await Assert.ThrowsAsync(() => - new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object).AuthenticateAsync(new Uri("http://www.idp.com"), new Uri("nyapp://callback"))); + new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object, mockActivator.Object).AuthenticateAsync(new Uri("http://www.idp.com"), new Uri("nyapp://callback"))); Assert.Equal("The WebAuthenticator requires a packaged app with an AppxManifest.", exception.Message); } @@ -71,13 +74,14 @@ public async void Should_Throw_When_Uri_Protocol_Not_Declared() var mockAppInstance = new Mock(); var mockHelpers = new Mock(); var mockTasksManager = new Mock(); + var mockActivator = new Mock(); - Activator.RedirectActivationCheck = true; + mockActivator.SetupGet(h => h.RedirectActivationChecked).Returns(true); mockHelpers.SetupGet(h => h.IsAppPackaged).Returns(true); mockHelpers.Setup(h => h.IsUriProtocolDeclared("myapp")).Returns(false); var exception = await Assert.ThrowsAsync(() => - new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object).AuthenticateAsync(new Uri("http://www.idp.com"), new Uri("myapp://callback"))); + new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object, mockActivator.Object).AuthenticateAsync(new Uri("http://www.idp.com"), new Uri("myapp://callback"))); Assert.Equal($"The URI Scheme myapp is not declared in AppxManifest.xml.", exception.Message); } @@ -88,14 +92,15 @@ public void Should_Open_Browser_With_State() var mockAppInstance = new Mock(); var mockHelpers = new Mock(); var mockTasksManager = new Mock(); + var mockActivator = new Mock(); - Activator.RedirectActivationCheck = true; + mockActivator.SetupGet(h => h.RedirectActivationChecked).Returns(true); mockHelpers.SetupGet(h => h.IsAppPackaged).Returns(true); mockHelpers.Setup(h => h.IsUriProtocolDeclared("myapp")).Returns(true); mockHelpers.Setup(h => h.OpenBrowser(It.IsAny())); mockAppInstance.Setup(a => a.GetCurrentAppKey()).Returns("test"); - var webAuthenticator = new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object); + var webAuthenticator = new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object, mockActivator.Object); // Do no await so we can leave the method again #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed @@ -113,8 +118,9 @@ public async void Should_return_WebAuthenticatorResult_With_Original_State() var mockHelpers = new Mock(); var mockTasksManager = new Mock(); Uri authorizeUri = new Uri("https://www.idp.com"); + var mockActivator = new Mock(); - Activator.RedirectActivationCheck = true; + mockActivator.SetupGet(h => h.RedirectActivationChecked).Returns(true); mockHelpers.SetupGet(h => h.IsAppPackaged).Returns(true); mockHelpers.Setup(h => h.IsUriProtocolDeclared("myapp")).Returns(true); mockHelpers.Setup(h => h.OpenBrowser(It.IsAny())).Callback((Uri uri) => @@ -130,7 +136,7 @@ public async void Should_return_WebAuthenticatorResult_With_Original_State() task.TrySetResult(new Uri($"myapp://callback?state={state}")); }); - var webAuthenticator = new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object); + var webAuthenticator = new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object, mockActivator.Object); var result = await webAuthenticator.AuthenticateAsync(new Uri("http://www.idp.com/?state=abc"), new Uri("myapp://callback")); Assert.NotNull(result); @@ -143,15 +149,16 @@ public void Should_Remove_Task_On_Cancel() var mockAppInstance = new Mock(); var mockHelpers = new Mock(); var mockTasksManager = new Mock(); + var mockActivator = new Mock(); - Activator.RedirectActivationCheck = true; + mockActivator.SetupGet(h => h.RedirectActivationChecked).Returns(true); mockHelpers.SetupGet(h => h.IsAppPackaged).Returns(true); mockHelpers.Setup(h => h.IsUriProtocolDeclared("myapp")).Returns(true); mockHelpers.Setup(h => h.OpenBrowser(It.IsAny())); mockAppInstance.Setup(a => a.GetCurrentAppKey()).Returns("test"); var cancellationTokenSource = new CancellationTokenSource(); - var webAuthenticator = new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object); + var webAuthenticator = new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object, mockActivator.Object); webAuthenticator.AuthenticateAsync(new Uri("http://www.idp.com"), new Uri("myapp://callback"), cancellationTokenSource.Token); @@ -166,15 +173,16 @@ public async void Should_Throw_If_Cancelled_Before() var mockAppInstance = new Mock(); var mockHelpers = new Mock(); var mockTasksManager = new Mock(); + var mockActivator = new Mock(); - Activator.RedirectActivationCheck = true; + mockActivator.SetupGet(h => h.RedirectActivationChecked).Returns(true); mockHelpers.SetupGet(h => h.IsAppPackaged).Returns(true); mockHelpers.Setup(h => h.IsUriProtocolDeclared("myapp")).Returns(true); mockHelpers.Setup(h => h.OpenBrowser(It.IsAny())); mockAppInstance.Setup(a => a.GetCurrentAppKey()).Returns("test"); var cancellationTokenSource = new CancellationTokenSource(); - var webAuthenticator = new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object); + var webAuthenticator = new WebAuthenticator(mockAppInstance.Object, mockHelpers.Object, mockTasksManager.Object, mockActivator.Object); cancellationTokenSource.Cancel(); @@ -189,13 +197,14 @@ public void Should_Resume_On_Activated() var mockAppInstance = new MockAppInstanceProxy(); var mockHelpers = new Mock(); var mockTasksManager = new Mock(); + var mockActivator = new Mock(); - Activator.RedirectActivationCheck = true; + mockActivator.SetupGet(h => h.RedirectActivationChecked).Returns(true); mockHelpers.SetupGet(h => h.IsAppPackaged).Returns(true); mockHelpers.Setup(h => h.IsUriProtocolDeclared("myapp")).Returns(true); mockHelpers.Setup(h => h.OpenBrowser(It.IsAny())); - new WebAuthenticator(mockAppInstance, mockHelpers.Object, mockTasksManager.Object); + new WebAuthenticator(mockAppInstance, mockHelpers.Object, mockTasksManager.Object, mockActivator.Object); var jsonObject = new JsonObject { @@ -254,6 +263,20 @@ public virtual string GetCurrentAppKey() return AppInstance.GetCurrent().Key; } + public Microsoft.Windows.AppLifecycle.AppActivationArguments GetCurrentActivatedEventArgs() + { + return null; + } + + public bool RedirectActivationToAsync(string key, Microsoft.Windows.AppLifecycle.AppActivationArguments activatedEventArgs) + { + return true; + } + + public void FindOrRegisterForKey() + { + + } } public class MockProtocolActivatedEventArgs : IProtocolActivatedEventArgs