diff --git a/CommunityToolkit.Authentication.Uwp/WindowsProvider.cs b/CommunityToolkit.Authentication.Uwp/WindowsProvider.cs index b899803..a7864c8 100644 --- a/CommunityToolkit.Authentication.Uwp/WindowsProvider.cs +++ b/CommunityToolkit.Authentication.Uwp/WindowsProvider.cs @@ -63,6 +63,11 @@ public class WindowsProvider : BaseProvider /// public WebAccountProviderConfig WebAccountProviderConfig => _webAccountProviderConfig; + /// + /// Gets or sets which DispatcherQueue is used to dispatch UI updates. + /// + public DispatcherQueue DispatcherQueue { get; set; } + /// /// Gets a cache of important values for the signed in user. /// @@ -71,7 +76,7 @@ public class WindowsProvider : BaseProvider private readonly string[] _scopes; private readonly AccountsSettingsPaneConfig? _accountsSettingsPaneConfig; private readonly WebAccountProviderConfig _webAccountProviderConfig; - private WebAccount _webAccount; + private WebAccount _webAccount = null; /// /// Initializes a new instance of the class. @@ -80,7 +85,8 @@ public class WindowsProvider : BaseProvider /// Configuration values for the AccountsSettingsPane. /// Configuration value for determining the available web account providers. /// Determines whether the provider attempts to silently log in upon construction. - public WindowsProvider(string[] scopes = null, WebAccountProviderConfig? webAccountProviderConfig = null, AccountsSettingsPaneConfig? accountsSettingsPaneConfig = null, bool autoSignIn = true) + /// The DispatcherQueue that should be used to dispatch UI updates, or null if this is being called from the UI thread. + public WindowsProvider(string[] scopes = null, WebAccountProviderConfig? webAccountProviderConfig = null, AccountsSettingsPaneConfig? accountsSettingsPaneConfig = null, bool autoSignIn = true, DispatcherQueue dispatcherQueue = null) { _scopes = scopes ?? DefaultScopes; _webAccountProviderConfig = webAccountProviderConfig ?? new WebAccountProviderConfig() @@ -89,7 +95,7 @@ public WindowsProvider(string[] scopes = null, WebAccountProviderConfig? webAcco }; _accountsSettingsPaneConfig = accountsSettingsPaneConfig; - _webAccount = null; + DispatcherQueue = dispatcherQueue ?? DispatcherQueue.GetForCurrentThread(); State = ProviderState.SignedOut; @@ -198,21 +204,7 @@ public override async Task GetTokenAsync(bool silentOnly = false) } // Attempt to authenticate interactively. - var tcs = new TaskCompletionSource(); - var taskQueued = DispatcherQueue.GetForCurrentThread().TryEnqueue(async () => - { - var result = await AuthenticateInteractiveAsync(_scopes); - tcs.SetResult(result); - }); - - if (taskQueued) - { - authResult = await tcs.Task; - } - else - { - tcs.SetCanceled(); - } + authResult = await AuthenticateInteractiveAsync(_scopes); } if (authResult?.ResponseStatus == WebTokenRequestStatus.Success) @@ -249,7 +241,7 @@ public override async Task GetTokenAsync(bool silentOnly = false) /// Display AccountSettingsPane for the management of logged-in users. /// /// . - public async Task ShowAccountManagementPaneAsync() + public Task ShowAccountManagementPaneAsync() { if (_webAccount == null) { @@ -257,7 +249,7 @@ public async Task ShowAccountManagementPaneAsync() } var tcs = new TaskCompletionSource(); - _ = DispatcherQueue.GetForCurrentThread().TryEnqueue(async () => + var taskQueued = DispatcherQueue.TryEnqueue(async () => { AccountsSettingsPane pane = null; try @@ -284,7 +276,12 @@ public async Task ShowAccountManagementPaneAsync() } }); - await tcs.Task; + if (!taskQueued) + { + tcs.SetException(new InvalidOperationException("Failed to enqueue the operation.")); + } + + return tcs.Task; } /// @@ -392,34 +389,45 @@ private async Task AuthenticateSilentAsync(string[] scope } } - private async Task AuthenticateInteractiveAsync(string[] scopes) + private Task AuthenticateInteractiveAsync(string[] scopes) { - try + var tcs = new TaskCompletionSource(); + var taskQueued = DispatcherQueue.TryEnqueue(async () => { - WebTokenRequestResult authResult = null; - - var account = _webAccount; - if (account != null) + try { - // We already have the account. - var webAccountProvider = account.WebAccountProvider; - var webTokenRequest = GetWebTokenRequest(webAccountProvider, _webAccountProviderConfig.ClientId, scopes); - authResult = await WebAuthenticationCoreManager.RequestTokenAsync(webTokenRequest, account); + WebTokenRequestResult authResult = null; + + var account = _webAccount; + if (account != null) + { + // We already have the account. + var webAccountProvider = account.WebAccountProvider; + var webTokenRequest = GetWebTokenRequest(webAccountProvider, _webAccountProviderConfig.ClientId, scopes); + authResult = await WebAuthenticationCoreManager.RequestTokenAsync(webTokenRequest, account); + } + else + { + // We don't have an account. Prompt the user to provide one. + var webAccountProvider = await ShowAccountSettingsPaneAndGetProviderAsync(); + var webTokenRequest = GetWebTokenRequest(webAccountProvider, _webAccountProviderConfig.ClientId, scopes); + authResult = await WebAuthenticationCoreManager.RequestTokenAsync(webTokenRequest); + } + + tcs.SetResult(authResult); } - else + catch (Exception e) { - // We don't have an account. Prompt the user to provide one. - var webAccountProvider = await ShowAccountSettingsPaneAndGetProviderAsync(); - var webTokenRequest = GetWebTokenRequest(webAccountProvider, _webAccountProviderConfig.ClientId, scopes); - authResult = await WebAuthenticationCoreManager.RequestTokenAsync(webTokenRequest); + tcs.SetException(e); } + }); - return authResult; - } - catch (HttpRequestException) + if (!taskQueued) { - throw; /* probably offline, no point continuing to interactive auth */ + tcs.SetException(new InvalidOperationException("Failed to enqueue the operation.")); } + + return tcs.Task; } /// diff --git a/CommunityToolkit.Graph.Uwp/CommunityToolkit.Graph.Uwp.csproj b/CommunityToolkit.Graph.Uwp/CommunityToolkit.Graph.Uwp.csproj index 2104762..97f8bac 100644 --- a/CommunityToolkit.Graph.Uwp/CommunityToolkit.Graph.Uwp.csproj +++ b/CommunityToolkit.Graph.Uwp/CommunityToolkit.Graph.Uwp.csproj @@ -22,7 +22,7 @@ - + diff --git a/CommunityToolkit.Graph.Uwp/Controls/GraphPresenter/GraphPresenter.cs b/CommunityToolkit.Graph.Uwp/Controls/GraphPresenter/GraphPresenter.cs index 484d24f..97ffd48 100644 --- a/CommunityToolkit.Graph.Uwp/Controls/GraphPresenter/GraphPresenter.cs +++ b/CommunityToolkit.Graph.Uwp/Controls/GraphPresenter/GraphPresenter.cs @@ -77,8 +77,7 @@ private async void GraphPresenter_Loaded(object sender, RoutedEventArgs e) var request = new BaseRequest( RequestBuilder.RequestUrl, RequestBuilder.Client); // TODO: Do we need separate Options here? - ////request.Method = HttpMethods.GET; - request.Method = "GET"; + request.Method = HttpMethods.GET; request.QueryOptions = QueryOptions?.Select(option => (Microsoft.Graph.QueryOption)option)?.ToList() ?? new List(); // Handle Special QueryOptions diff --git a/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj b/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj index 8d501e3..0f03508 100644 --- a/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj +++ b/CommunityToolkit.Graph/CommunityToolkit.Graph.csproj @@ -13,7 +13,7 @@ - + diff --git a/SampleTest/App.xaml.cs b/SampleTest/App.xaml.cs index b12e662..1b89760 100644 --- a/SampleTest/App.xaml.cs +++ b/SampleTest/App.xaml.cs @@ -24,12 +24,8 @@ sealed partial class App : Application public App() { this.InitializeComponent(); - this.Suspending += OnSuspending; } - // Which provider should be used for authentication? - private readonly ProviderType _providerType = ProviderType.Mock; - // List of available authentication providers. private enum ProviderType { @@ -38,42 +34,42 @@ private enum ProviderType Windows } + // Which provider should be used for authentication? + private readonly ProviderType _providerType = ProviderType.Mock; + /// /// Initialize the global authentication provider. /// - private async void InitializeGlobalProvider() + private void InitializeGlobalProvider() { if (ProviderManager.Instance.GlobalProvider != null) { return; } - await Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => - { - // Provider config - string clientId = "YOUR_CLIENT_ID_HERE"; - string[] scopes = { "User.Read", "User.ReadBasic.All", "People.Read", "Calendars.Read", "Mail.Read", "Group.Read.All", "ChannelMessage.Read.All" }; - bool autoSignIn = true; + // Provider config + string clientId = "YOUR_CLIENT_ID_HERE"; + string[] scopes = { "User.Read", "User.ReadBasic.All", "People.Read", "Calendars.Read", "Mail.Read", "Group.Read.All", "ChannelMessage.Read.All" }; + bool autoSignIn = true; - switch (_providerType) - { - // Mock provider - case ProviderType.Mock: - ProviderManager.Instance.GlobalProvider = new MockProvider(signedIn: autoSignIn); - break; - - // Msal provider - case ProviderType.Msal: - ProviderManager.Instance.GlobalProvider = new MsalProvider(clientId: clientId, scopes: scopes, autoSignIn: autoSignIn); - break; - - // Windows provider - case ProviderType.Windows: - var webAccountProviderConfig = new WebAccountProviderConfig(WebAccountProviderType.Msa, clientId); - ProviderManager.Instance.GlobalProvider = new WindowsProvider(scopes, webAccountProviderConfig: webAccountProviderConfig, autoSignIn: autoSignIn); - break; - } - }); + switch (_providerType) + { + // Mock provider + case ProviderType.Mock: + ProviderManager.Instance.GlobalProvider = new MockProvider(signedIn: autoSignIn); + break; + + // Msal provider + case ProviderType.Msal: + ProviderManager.Instance.GlobalProvider = new MsalProvider(clientId: clientId, scopes: scopes, autoSignIn: autoSignIn); + break; + + // Windows provider + case ProviderType.Windows: + var webAccountProviderConfig = new WebAccountProviderConfig(WebAccountProviderType.Msa, clientId); + ProviderManager.Instance.GlobalProvider = new WindowsProvider(scopes, webAccountProviderConfig: webAccountProviderConfig, autoSignIn: autoSignIn); + break; + } } /// @@ -84,22 +80,9 @@ await Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority. protected override void OnLaunched(LaunchActivatedEventArgs e) { Frame rootFrame = Window.Current.Content as Frame; - - // Do not repeat app initialization when the Window already has content, - // just ensure that the window is active if (rootFrame == null) { - // Create a Frame to act as the navigation context and navigate to the first page rootFrame = new Frame(); - - rootFrame.NavigationFailed += OnNavigationFailed; - - if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) - { - //TODO: Load state from previously suspended application - } - - // Place the frame in the current Window Window.Current.Content = rootFrame; } @@ -107,40 +90,13 @@ protected override void OnLaunched(LaunchActivatedEventArgs e) { if (rootFrame.Content == null) { - // When the navigation stack isn't restored navigate to the first page, - // configuring the new page by passing required information as a navigation - // parameter rootFrame.Navigate(typeof(MainPage), e.Arguments); } - // Ensure the current window is active + Window.Current.Activate(); InitializeGlobalProvider(); } } - - /// - /// Invoked when Navigation to a certain page fails - /// - /// The Frame which failed navigation - /// Details about the navigation failure - void OnNavigationFailed(object sender, NavigationFailedEventArgs e) - { - throw new Exception("Failed to load Page " + e.SourcePageType.FullName); - } - - /// - /// Invoked when application execution is being suspended. Application state is saved - /// without knowing whether the application will be terminated or resumed with the contents - /// of memory still intact. - /// - /// The source of the suspend request. - /// Details about the suspend request. - private void OnSuspending(object sender, SuspendingEventArgs e) - { - var deferral = e.SuspendingOperation.GetDeferral(); - //TODO: Save application state and stop any background activity - deferral.Complete(); - } } } diff --git a/SampleTest/SampleTest.csproj b/SampleTest/SampleTest.csproj index 75520a4..64b2ca8 100644 --- a/SampleTest/SampleTest.csproj +++ b/SampleTest/SampleTest.csproj @@ -168,7 +168,7 @@ - 3.31.0 + 4.0.0 6.2.12 diff --git a/Samples/ManualGraphRequestSample/App.xaml.cs b/Samples/ManualGraphRequestSample/App.xaml.cs index 8c872a2..51fba54 100644 --- a/Samples/ManualGraphRequestSample/App.xaml.cs +++ b/Samples/ManualGraphRequestSample/App.xaml.cs @@ -31,6 +31,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs e) { rootFrame.Navigate(typeof(MainPage), e.Arguments); } + Window.Current.Activate(); } } diff --git a/Samples/UwpMsalProviderSample/App.xaml.cs b/Samples/UwpMsalProviderSample/App.xaml.cs index 743e5a5..53c1340 100644 --- a/Samples/UwpMsalProviderSample/App.xaml.cs +++ b/Samples/UwpMsalProviderSample/App.xaml.cs @@ -22,15 +22,12 @@ public App() /// void ConfigureGlobalProvider() { - DispatcherQueue.GetForCurrentThread().TryEnqueue(DispatcherQueuePriority.Normal, () => + if (ProviderManager.Instance.GlobalProvider == null) { - if (ProviderManager.Instance.GlobalProvider == null) - { - string clientId = "YOUR-CLIENT-ID-HERE"; - string[] scopes = new string[] { "User.Read" }; - ProviderManager.Instance.GlobalProvider = new MsalProvider(clientId, scopes); - } - }); + string clientId = "YOUR-CLIENT-ID-HERE"; + string[] scopes = new string[] { "User.Read" }; + ProviderManager.Instance.GlobalProvider = new MsalProvider(clientId, scopes); + } } protected override void OnLaunched(LaunchActivatedEventArgs e) @@ -48,6 +45,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs e) { rootFrame.Navigate(typeof(MainPage), e.Arguments); } + Window.Current.Activate(); ConfigureGlobalProvider(); diff --git a/Samples/UwpWindowsProviderSample/App.xaml.cs b/Samples/UwpWindowsProviderSample/App.xaml.cs index 4e2dcd0..fc2a9fc 100644 --- a/Samples/UwpWindowsProviderSample/App.xaml.cs +++ b/Samples/UwpWindowsProviderSample/App.xaml.cs @@ -29,15 +29,12 @@ public App() /// void ConfigureGlobalProvider() { - DispatcherQueue.GetForCurrentThread().TryEnqueue(DispatcherQueuePriority.Normal, () => + if (ProviderManager.Instance.GlobalProvider == null) { - if (ProviderManager.Instance.GlobalProvider == null) - { - string[] scopes = new string[] { "User.Read" }; - var paneConfig = GetAccountsSettingsPaneConfig(); - ProviderManager.Instance.GlobalProvider = new WindowsProvider(scopes, accountsSettingsPaneConfig: paneConfig); - } - }); + string[] scopes = new string[] { "User.Read" }; + var paneConfig = GetAccountsSettingsPaneConfig(); + ProviderManager.Instance.GlobalProvider = new WindowsProvider(scopes, accountsSettingsPaneConfig: paneConfig); + } } AccountsSettingsPaneConfig GetAccountsSettingsPaneConfig() diff --git a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj index 9fde621..4547df5 100644 --- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj +++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj @@ -160,7 +160,7 @@ 5.10.3 - 1.25.1 + 2.0.0 6.2.12