Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 48 additions & 40 deletions CommunityToolkit.Authentication.Uwp/WindowsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public class WindowsProvider : BaseProvider
/// </summary>
public WebAccountProviderConfig WebAccountProviderConfig => _webAccountProviderConfig;

/// <summary>
/// Gets or sets which DispatcherQueue is used to dispatch UI updates.
/// </summary>
public DispatcherQueue DispatcherQueue { get; set; }

/// <summary>
/// Gets a cache of important values for the signed in user.
/// </summary>
Expand All @@ -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;

/// <summary>
/// Initializes a new instance of the <see cref="WindowsProvider"/> class.
Expand All @@ -80,7 +85,8 @@ public class WindowsProvider : BaseProvider
/// <param name="accountsSettingsPaneConfig">Configuration values for the AccountsSettingsPane.</param>
/// <param name="webAccountProviderConfig">Configuration value for determining the available web account providers.</param>
/// <param name="autoSignIn">Determines whether the provider attempts to silently log in upon construction.</param>
public WindowsProvider(string[] scopes = null, WebAccountProviderConfig? webAccountProviderConfig = null, AccountsSettingsPaneConfig? accountsSettingsPaneConfig = null, bool autoSignIn = true)
/// <param name="dispatcherQueue">The DispatcherQueue that should be used to dispatch UI updates, or null if this is being called from the UI thread.</param>
public WindowsProvider(string[] scopes = null, WebAccountProviderConfig? webAccountProviderConfig = null, AccountsSettingsPaneConfig? accountsSettingsPaneConfig = null, bool autoSignIn = true, DispatcherQueue dispatcherQueue = null)
{
_scopes = scopes ?? DefaultScopes;
_webAccountProviderConfig = webAccountProviderConfig ?? new WebAccountProviderConfig()
Expand All @@ -89,7 +95,7 @@ public WindowsProvider(string[] scopes = null, WebAccountProviderConfig? webAcco
};
_accountsSettingsPaneConfig = accountsSettingsPaneConfig;

_webAccount = null;
DispatcherQueue = dispatcherQueue ?? DispatcherQueue.GetForCurrentThread();

State = ProviderState.SignedOut;

Expand Down Expand Up @@ -198,21 +204,7 @@ public override async Task<string> GetTokenAsync(bool silentOnly = false)
}

// Attempt to authenticate interactively.
var tcs = new TaskCompletionSource<WebTokenRequestResult>();
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)
Expand Down Expand Up @@ -249,15 +241,15 @@ public override async Task<string> GetTokenAsync(bool silentOnly = false)
/// Display AccountSettingsPane for the management of logged-in users.
/// </summary>
/// <returns><see cref="Task"/>.</returns>
public async Task ShowAccountManagementPaneAsync()
public Task ShowAccountManagementPaneAsync()
{
if (_webAccount == null)
{
throw new InvalidOperationException("A logged in account is required to display the account management pane.");
}

var tcs = new TaskCompletionSource<bool>();
_ = DispatcherQueue.GetForCurrentThread().TryEnqueue(async () =>
var taskQueued = DispatcherQueue.TryEnqueue(async () =>
{
AccountsSettingsPane pane = null;
try
Expand All @@ -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;
}

/// <summary>
Expand Down Expand Up @@ -392,34 +389,45 @@ private async Task<WebTokenRequestResult> AuthenticateSilentAsync(string[] scope
}
}

private async Task<WebTokenRequestResult> AuthenticateInteractiveAsync(string[] scopes)
private Task<WebTokenRequestResult> AuthenticateInteractiveAsync(string[] scopes)
{
try
var tcs = new TaskCompletionSource<WebTokenRequestResult>();
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;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Graph" Version="3.31.0" />
<PackageReference Include="Microsoft.Graph" Version="4.0.0" />
<PackageReference Include="Microsoft.Toolkit.Uwp.UI" Version="7.0.1" />
<PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls.Input" Version="7.0.1" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Microsoft.Graph.QueryOption>();

// Handle Special QueryOptions
Expand Down
2 changes: 1 addition & 1 deletion CommunityToolkit.Graph/CommunityToolkit.Graph.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Graph" Version="3.31.0" />
<PackageReference Include="Microsoft.Graph" Version="4.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
98 changes: 27 additions & 71 deletions SampleTest/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -38,42 +34,42 @@ private enum ProviderType
Windows
}

// Which provider should be used for authentication?
private readonly ProviderType _providerType = ProviderType.Mock;

/// <summary>
/// Initialize the global authentication provider.
/// </summary>
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;
}
}

/// <summary>
Expand All @@ -84,63 +80,23 @@ 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;
}

if (e.PrelaunchActivated == false)
{
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();
}
}

/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}

/// <summary>
/// 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.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
}
}
2 changes: 1 addition & 1 deletion SampleTest/SampleTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Graph">
<Version>3.31.0</Version>
<Version>4.0.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.12</Version>
Expand Down
1 change: 1 addition & 0 deletions Samples/ManualGraphRequestSample/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}

Window.Current.Activate();
}
}
Expand Down
14 changes: 6 additions & 8 deletions Samples/UwpMsalProviderSample/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,12 @@ public App()
/// </summary>
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)
Expand All @@ -48,6 +45,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}

Window.Current.Activate();

ConfigureGlobalProvider();
Expand Down
Loading