Update MsalProvider to use native account broker (#169)

* Updated MsalProvider to use local account broker when possible.

* Updated MsalProvider configuration to support toggling org account login.

* Re-adding SDK version header logic

* Added token cache example to wpf sample app.

* Renamed WpfMsalProviderSample to WpfNet5WindowsMsalProviderSample and added WpfNetCoreMsalProviderSample
This commit is contained in:
Shane Weaver 2021-10-25 13:56:19 -07:00 коммит произвёл GitHub
Родитель 69197dd945
Коммит 33f781a577
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
27 изменённых файлов: 554 добавлений и 77 удалений

Просмотреть файл

@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;uap10.0;net5.0-windows10.0.17763.0;netcoreapp3.1</TargetFrameworks>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>7</SupportedOSPlatformVersion>
<Title>Windows Community Toolkit .NET Standard Auth Services</Title>
<Description>
@ -18,6 +20,10 @@
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="2.19.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<PackageReference Include="Microsoft.Identity.Client.Desktop" Version="4.37.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommunityToolkit.Authentication\CommunityToolkit.Authentication.csproj" />
</ItemGroup>

Просмотреть файл

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
@ -11,6 +12,16 @@ using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.Identity.Client;
#if WINDOWS_UWP
using Windows.Security.Authentication.Web;
#else
using System.Diagnostics;
#endif
#if NETCOREAPP3_1
using Microsoft.Identity.Client.Desktop;
#endif
namespace CommunityToolkit.Authentication
{
/// <summary>
@ -18,52 +29,73 @@ namespace CommunityToolkit.Authentication
/// </summary>
public class MsalProvider : BaseProvider
{
/// <summary>
/// A prefix value used to create the redirect URI value for use in AAD.
/// </summary>
public static readonly string MSAccountBrokerRedirectUriPrefix = "ms-appx-web://microsoft.aad.brokerplugin/";
private static readonly SemaphoreSlim SemaphoreSlim = new (1);
/// <summary>
/// Gets or sets the currently authenticated user account.
/// </summary>
public IAccount Account { get; protected set; }
/// <inheritdoc />
public override string CurrentAccountId => _account?.HomeAccountId?.Identifier;
public override string CurrentAccountId => Account?.HomeAccountId?.Identifier;
/// <summary>
/// Gets the MSAL.NET Client used to authenticate the user.
/// Gets or sets the MSAL.NET Client used to authenticate the user.
/// </summary>
protected IPublicClientApplication Client { get; private set; }
public IPublicClientApplication Client { get; protected set; }
/// <summary>
/// Gets an array of scopes to use for accessing Graph resources.
/// </summary>
protected string[] Scopes { get; private set; }
private IAccount _account;
protected string[] Scopes { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MsalProvider"/> class.
/// Initializes a new instance of the <see cref="MsalProvider"/> class using a configuration object.
/// </summary>
/// <param name="clientId">Registered ClientId.</param>
/// <param name="redirectUri">RedirectUri for auth response.</param>
/// <param name="client">Registered ClientId in Azure Acitve Directory.</param>
/// <param name="scopes">List of Scopes to initially request.</param>
/// <param name="autoSignIn">Determines whether the provider attempts to silently log in upon instantionation.</param>
public MsalProvider(string clientId, string[] scopes = null, string redirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient", bool autoSignIn = true)
/// <param name="autoSignIn">Determines whether the provider attempts to silently log in upon creation.</param>
public MsalProvider(IPublicClientApplication client, string[] scopes = null, bool autoSignIn = true)
{
var client = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(AzureCloudInstance.AzurePublic, AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount)
.WithRedirectUri(redirectUri)
.WithClientName(ProviderManager.ClientName)
.WithClientVersion(Assembly.GetExecutingAssembly().GetName().Version.ToString())
.Build();
Scopes = scopes.Select(s => s.ToLower()).ToArray() ?? new string[] { string.Empty };
Client = client;
Scopes = scopes.Select(s => s.ToLower()).ToArray() ?? new string[] { string.Empty };
if (autoSignIn)
{
_ = TrySilentSignInAsync();
TrySilentSignInAsync();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="MsalProvider"/> class with default configuration values.
/// </summary>
/// <param name="clientId">Registered client id in Azure Acitve Directory.</param>
/// <param name="redirectUri">RedirectUri for auth response.</param>
/// <param name="scopes">List of Scopes to initially request.</param>
/// <param name="autoSignIn">Determines whether the provider attempts to silently log in upon creation.</param>
/// <param name="listWindowsWorkAndSchoolAccounts">Determines if organizational accounts should be enabled/disabled.</param>
/// <param name="tenantId">Registered tenant id in Azure Active Directory.</param>
public MsalProvider(string clientId, string[] scopes = null, string redirectUri = null, bool autoSignIn = true, bool listWindowsWorkAndSchoolAccounts = true, string tenantId = null)
{
Client = CreatePublicClientApplication(clientId, tenantId, redirectUri, listWindowsWorkAndSchoolAccounts);
Scopes = scopes.Select(s => s.ToLower()).ToArray() ?? new string[] { string.Empty };
if (autoSignIn)
{
TrySilentSignInAsync();
}
}
/// <inheritdoc/>
public override async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
AddSdkVersion(request);
string token;
// Check if any specific scopes are being requested.
@ -87,7 +119,7 @@ namespace CommunityToolkit.Authentication
/// <inheritdoc/>
public override async Task<bool> TrySilentSignInAsync()
{
if (_account != null && State == ProviderState.SignedIn)
if (Account != null && State == ProviderState.SignedIn)
{
return true;
}
@ -108,7 +140,7 @@ namespace CommunityToolkit.Authentication
/// <inheritdoc/>
public override async Task SignInAsync()
{
if (_account != null || State != ProviderState.SignedOut)
if (Account != null || State != ProviderState.SignedOut)
{
return;
}
@ -129,10 +161,10 @@ namespace CommunityToolkit.Authentication
/// <inheritdoc />
public override async Task SignOutAsync()
{
if (_account != null)
if (Account != null)
{
await Client.RemoveAsync(_account);
_account = null;
await Client.RemoveAsync(Account);
Account = null;
}
State = ProviderState.SignedOut;
@ -144,7 +176,48 @@ namespace CommunityToolkit.Authentication
return this.GetTokenWithScopesAsync(Scopes, silentOnly);
}
private async Task<string> GetTokenWithScopesAsync(string[] scopes, bool silentOnly = false)
/// <summary>
/// Create an instance of <see cref="PublicClientApplication"/> using the provided config and some default values.
/// </summary>
/// <param name="clientId">Registered ClientId.</param>
/// <param name="tenantId">An optional tenant id.</param>
/// <param name="redirectUri">Redirect uri for auth response.</param>
/// <param name="listWindowsWorkAndSchoolAccounts">Determines if organizational accounts should be supported.</param>
/// <returns>A new instance of <see cref="PublicClientApplication"/>.</returns>
protected IPublicClientApplication CreatePublicClientApplication(string clientId, string tenantId, string redirectUri, bool listWindowsWorkAndSchoolAccounts)
{
var authority = listWindowsWorkAndSchoolAccounts ? AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount : AadAuthorityAudience.PersonalMicrosoftAccount;
var clientBuilder = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(AzureCloudInstance.AzurePublic, authority)
.WithClientName(ProviderManager.ClientName)
.WithClientVersion(Assembly.GetExecutingAssembly().GetName().Version.ToString());
if (tenantId != null)
{
clientBuilder = clientBuilder.WithTenantId(tenantId);
}
#if WINDOWS_UWP || NET5_0_WINDOWS10_0_17763_0
clientBuilder = clientBuilder.WithBroker();
#elif NETCOREAPP3_1
clientBuilder = clientBuilder.WithWindowsBroker();
#endif
clientBuilder = (redirectUri != null)
? clientBuilder.WithRedirectUri(redirectUri)
: clientBuilder.WithDefaultRedirectUri();
return clientBuilder.Build();
}
/// <summary>
/// Retrieve an authorization token using the provided scopes.
/// </summary>
/// <param name="scopes">An array of scopes to pass along with the Graph request.</param>
/// <param name="silentOnly">A value to determine whether account broker UI should be shown, if required by MSAL.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
protected async Task<string> GetTokenWithScopesAsync(string[] scopes, bool silentOnly = false)
{
await SemaphoreSlim.WaitAsync();
@ -153,7 +226,7 @@ namespace CommunityToolkit.Authentication
AuthenticationResult authResult = null;
try
{
var account = _account ?? (await Client.GetAccountsAsync()).FirstOrDefault();
var account = Account ?? (await Client.GetAccountsAsync()).FirstOrDefault();
if (account != null)
{
authResult = await Client.AcquireTokenSilent(scopes, account).ExecuteAsync();
@ -172,14 +245,26 @@ namespace CommunityToolkit.Authentication
{
try
{
if (_account != null)
var paramBuilder = Client.AcquireTokenInteractive(scopes);
if (Account != null)
{
authResult = await Client.AcquireTokenInteractive(scopes).WithPrompt(Prompt.NoPrompt).WithAccount(_account).ExecuteAsync();
}
else
{
authResult = await Client.AcquireTokenInteractive(scopes).WithPrompt(Prompt.NoPrompt).ExecuteAsync();
paramBuilder = paramBuilder.WithAccount(Account);
}
#if WINDOWS_UWP
// For UWP, specify NoPrompt for the least intrusive user experience.
paramBuilder = paramBuilder.WithPrompt(Prompt.NoPrompt);
#else
// Otherwise, get the process by FriendlyName from Application Domain
var friendlyName = AppDomain.CurrentDomain.FriendlyName;
var proc = Process.GetProcessesByName(friendlyName).First();
var windowHandle = proc.MainWindowHandle;
paramBuilder = paramBuilder.WithParentActivityOrWindow(windowHandle);
#endif
authResult = await paramBuilder.ExecuteAsync();
}
catch
{
@ -188,7 +273,7 @@ namespace CommunityToolkit.Authentication
}
}
_account = authResult?.Account;
Account = authResult?.Account;
return authResult?.AccessToken;
}

Просмотреть файл

@ -0,0 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Identity.Client.Extensions.Msal;
namespace CommunityToolkit.Authentication.Extensions
{
/// <summary>
/// Helpers for working with the MsalProvider.
/// </summary>
public static class MsalProviderExtensions
{
/// <summary>
/// Helper function to initialize the token cache for non-UWP apps. MSAL handles this automatically on UWP.
/// </summary>
/// <param name="provider">The instance of <see cref="MsalProvider"/> to init the cache for.</param>
/// <param name="storageProperties">Properties for configuring the storage cache.</param>
/// <param name="logger">Passing null uses the default TraceSource logger.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
public static async Task InitTokenCacheAsync(
this MsalProvider provider,
StorageCreationProperties storageProperties,
TraceSource logger = null)
{
#if !WINDOWS_UWP
// Token cache persistence (not required on UWP as MSAL does it for you)
var cacheHelper = await MsalCacheHelper.CreateAsync(storageProperties, logger);
cacheHelper.RegisterCache(provider.Client.UserTokenCache);
#endif
}
}
}

Просмотреть файл

@ -110,6 +110,8 @@ namespace CommunityToolkit.Authentication
/// <inheritdoc />
public override async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
AddSdkVersion(request);
string token = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue(AuthenticationHeaderScheme, token);
}

Просмотреть файл

@ -71,7 +71,7 @@ namespace CommunityToolkit.Graph.Extensions
.Photo
.Content
.Request()
.WithScopes(new string[] { "user.readbasic.all" })
.WithScopes(new string[] { "user.read" })
.GetAsync();
}

Просмотреть файл

@ -155,7 +155,6 @@
</ItemGroup>
<ItemGroup>
<Content Include="Assets\FileIcon.png" />
<None Include="Package.StoreAssociation.xml" />
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />

Просмотреть файл

@ -1,26 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Authentication;
using System;
using System.Windows;
namespace WpfMsalProviderSample
{
public partial class App : Application
{
protected override void OnActivated(EventArgs e)
{
if (ProviderManager.Instance.GlobalProvider == null)
{
string clientId = "YOUR-CLIENT-ID-HERE";
string[] scopes = new string[] { "User.Read" };
string redirectUri = "http://localhost";
ProviderManager.Instance.GlobalProvider = new MsalProvider(clientId, scopes, redirectUri);
}
base.OnActivated(e);
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
<Application x:Class="WpfNet5WindowsMsalProviderSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfNet5WindowsMsalProviderSample"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

Просмотреть файл

@ -0,0 +1,51 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Authentication;
using CommunityToolkit.Authentication.Extensions;
using Microsoft.Identity.Client.Extensions.Msal;
namespace WpfNet5WindowsMsalProviderSample
{
public partial class App : Application
{
static readonly string ClientId = "YOUR-CLIENT-ID-HERE";
static readonly string[] Scopes = new string[] { "User.Read" };
protected override void OnActivated(EventArgs e)
{
InitializeGlobalProviderAsync();
base.OnActivated(e);
}
private async Task InitializeGlobalProviderAsync()
{
if (ProviderManager.Instance.GlobalProvider == null)
{
var provider = new MsalProvider(ClientId, Scopes, null, false, true);
// Configure the token cache storage for non-UWP applications.
var storageProperties = new StorageCreationPropertiesBuilder(CacheConfig.CacheFileName, CacheConfig.CacheDir)
.WithLinuxKeyring(
CacheConfig.LinuxKeyRingSchema,
CacheConfig.LinuxKeyRingCollection,
CacheConfig.LinuxKeyRingLabel,
CacheConfig.LinuxKeyRingAttr1,
CacheConfig.LinuxKeyRingAttr2)
.WithMacKeyChain(
CacheConfig.KeyChainServiceName,
CacheConfig.KeyChainAccountName)
.Build();
await provider.InitTokenCacheAsync(storageProperties);
ProviderManager.Instance.GlobalProvider = provider;
await provider.TrySilentSignInAsync();
}
}
}
}

Просмотреть файл

@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using Microsoft.Identity.Client.Extensions.Msal;
namespace WpfNet5WindowsMsalProviderSample
{
/// <summary>
/// https://github.com/AzureAD/microsoft-authentication-extensions-for-dotnet/wiki/Cross-platform-Token-Cache
/// </summary>
class CacheConfig
{
public const string CacheFileName = "msal_cache.dat";
public const string CacheDir = "MSAL_CACHE";
public const string KeyChainServiceName = "msal_service";
public const string KeyChainAccountName = "msal_account";
public const string LinuxKeyRingSchema = "com.contoso.devtools.tokencache";
public const string LinuxKeyRingCollection = MsalCacheHelper.LinuxKeyRingDefaultCollection;
public const string LinuxKeyRingLabel = "MSAL token cache for all Contoso dev tool apps.";
public static readonly KeyValuePair<string, string> LinuxKeyRingAttr1 = new KeyValuePair<string, string>("Version", "1");
public static readonly KeyValuePair<string, string> LinuxKeyRingAttr2 = new KeyValuePair<string, string>("ProductGroup", "MyApps");
}
}

Просмотреть файл

@ -0,0 +1,14 @@
<UserControl x:Class="WpfNet5WindowsMsalProviderSample.LoginButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfNet5WindowsMsalProviderSample"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Button x:Name="MyButton"
Click="MyButton_Click"
IsEnabled="{Binding IsEnabled}" />
</Grid>
</UserControl>

Просмотреть файл

@ -0,0 +1,70 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Authentication;
using System;
using System.Windows;
using System.Windows.Controls;
namespace WpfNet5WindowsMsalProviderSample
{
/// <summary>
/// A simple button for triggering the globally configured IProvider to sign in and out.
/// </summary>
public partial class LoginButton : UserControl
{
public LoginButton()
{
InitializeComponent();
ProviderManager.Instance.ProviderStateChanged += (s, e) => UpdateState();
UpdateState();
}
private void UpdateState()
{
var provider = ProviderManager.Instance.GlobalProvider;
if (provider == null || provider.State == ProviderState.Loading)
{
MyButton.Content = "Sign in";
IsEnabled = false;
return;
}
switch (provider.State)
{
case ProviderState.SignedIn:
MyButton.Content = "Sign out";
break;
case ProviderState.SignedOut:
MyButton.Content = "Sign in";
break;
}
IsEnabled = true;
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
Application.Current.Dispatcher.Invoke(new Action(() =>
{
var provider = ProviderManager.Instance.GlobalProvider;
if (provider != null)
{
switch (provider.State)
{
case ProviderState.SignedOut:
provider.SignInAsync();
break;
case ProviderState.SignedIn:
provider.SignOutAsync();
break;
}
}
}));
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
<Window x:Class="WpfNet5WindowsMsalProviderSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfNet5WindowsMsalProviderSample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<local:LoginButton />
<TextBlock x:Name="SignedInUserTextBlock" />
</StackPanel>
</Window>

Просмотреть файл

@ -6,7 +6,7 @@ using CommunityToolkit.Authentication;
using CommunityToolkit.Graph.Extensions;
using System.Windows;
namespace WpfMsalProviderSample
namespace WpfNet5WindowsMsalProviderSample
{
/// <summary>
/// Interaction logic for MainWindow.xaml

Просмотреть файл

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows10.0.17763.0</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="2.19.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\CommunityToolkit.Authentication\CommunityToolkit.Authentication.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Authentication.Msal\CommunityToolkit.Authentication.Msal.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Graph\CommunityToolkit.Graph.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -1,7 +1,7 @@
<Application x:Class="WpfMsalProviderSample.App"
<Application x:Class="WpfNetCoreMsalProviderSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfMsalProviderSample"
xmlns:local="clr-namespace:WpfNetCoreMsalProviderSample"
StartupUri="MainWindow.xaml">
<Application.Resources>

Просмотреть файл

@ -0,0 +1,51 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Authentication;
using CommunityToolkit.Authentication.Extensions;
using Microsoft.Identity.Client.Extensions.Msal;
namespace WpfNetCoreMsalProviderSample
{
public partial class App : Application
{
static readonly string ClientId = "YOUR-CLIENT-ID-HERE";
static readonly string[] Scopes = new string[] { "User.Read" };
protected override void OnActivated(EventArgs e)
{
InitializeGlobalProviderAsync();
base.OnActivated(e);
}
private async Task InitializeGlobalProviderAsync()
{
if (ProviderManager.Instance.GlobalProvider == null)
{
var provider = new MsalProvider(ClientId, Scopes, null, false, true);
// Configure the token cache storage for non-UWP applications.
var storageProperties = new StorageCreationPropertiesBuilder(CacheConfig.CacheFileName, CacheConfig.CacheDir)
.WithLinuxKeyring(
CacheConfig.LinuxKeyRingSchema,
CacheConfig.LinuxKeyRingCollection,
CacheConfig.LinuxKeyRingLabel,
CacheConfig.LinuxKeyRingAttr1,
CacheConfig.LinuxKeyRingAttr2)
.WithMacKeyChain(
CacheConfig.KeyChainServiceName,
CacheConfig.KeyChainAccountName)
.Build();
await provider.InitTokenCacheAsync(storageProperties);
ProviderManager.Instance.GlobalProvider = provider;
await provider.TrySilentSignInAsync();
}
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

Просмотреть файл

@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using Microsoft.Identity.Client.Extensions.Msal;
namespace WpfNetCoreMsalProviderSample
{
/// <summary>
/// https://github.com/AzureAD/microsoft-authentication-extensions-for-dotnet/wiki/Cross-platform-Token-Cache
/// </summary>
class CacheConfig
{
public const string CacheFileName = "msal_cache.dat";
public const string CacheDir = "MSAL_CACHE";
public const string KeyChainServiceName = "msal_service";
public const string KeyChainAccountName = "msal_account";
public const string LinuxKeyRingSchema = "com.contoso.devtools.tokencache";
public const string LinuxKeyRingCollection = MsalCacheHelper.LinuxKeyRingDefaultCollection;
public const string LinuxKeyRingLabel = "MSAL token cache for all Contoso dev tool apps.";
public static readonly KeyValuePair<string, string> LinuxKeyRingAttr1 = new KeyValuePair<string, string>("Version", "1");
public static readonly KeyValuePair<string, string> LinuxKeyRingAttr2 = new KeyValuePair<string, string>("ProductGroup", "MyApps");
}
}

Просмотреть файл

@ -1,9 +1,9 @@
<UserControl x:Class="WpfMsalProviderSample.LoginButton"
<UserControl x:Class="WpfNetCoreMsalProviderSample.LoginButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfMsalProviderSample"
xmlns:local="clr-namespace:WpfNetCoreMsalProviderSample"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>

Просмотреть файл

@ -2,12 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Authentication;
using System;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Authentication;
namespace WpfMsalProviderSample
namespace WpfNetCoreMsalProviderSample
{
/// <summary>
/// A simple button for triggering the globally configured IProvider to sign in and out.

Просмотреть файл

@ -1,9 +1,9 @@
<Window x:Class="WpfMsalProviderSample.MainWindow"
<Window x:Class="WpfNetCoreMsalProviderSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfMsalProviderSample"
xmlns:local="clr-namespace:WpfNetCoreMsalProviderSample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>

Просмотреть файл

@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Windows;
using CommunityToolkit.Authentication;
using CommunityToolkit.Graph.Extensions;
namespace WpfNetCoreMsalProviderSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ProviderManager.Instance.ProviderStateChanged += OnProviderStateChanged;
}
private async void OnProviderStateChanged(object sender, ProviderStateChangedEventArgs e)
{
if (e.NewState == ProviderState.SignedIn)
{
SignedInUserTextBlock.Text = "Signed in as...";
var graphClient = ProviderManager.Instance.GlobalProvider.GetClient();
var me = await graphClient.Me.Request().GetAsync();
SignedInUserTextBlock.Text = "Signed in as: " + me.DisplayName;
}
else
{
SignedInUserTextBlock.Text = "Please sign in.";
}
}
}
}

Просмотреть файл

@ -4,11 +4,12 @@
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
<DeterministicSourcePaths Condition="'$(EnableSourceLink)' == ''">false</DeterministicSourcePaths>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\CommunityToolkit.Authentication\CommunityToolkit.Authentication.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Authentication.Msal\CommunityToolkit.Authentication.Msal.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Authentication\CommunityToolkit.Authentication.csproj" />
<ProjectReference Include="..\..\CommunityToolkit.Graph\CommunityToolkit.Graph.csproj" />
</ItemGroup>

Просмотреть файл

@ -43,12 +43,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{022B
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UwpMsalProviderSample", "Samples\UwpMsalProviderSample\UwpMsalProviderSample.csproj", "{D0F6A1EB-806E-424A-BDCA-9F749F12774F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfMsalProviderSample", "Samples\WpfMsalProviderSample\WpfMsalProviderSample.csproj", "{EDAD72A8-498B-4645-AD1A-E5CDBDB610F7}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfNet5WindowsMsalProviderSample", "Samples\WpfNet5WindowsMsalProviderSample\WpfNet5WindowsMsalProviderSample.csproj", "{EDAD72A8-498B-4645-AD1A-E5CDBDB610F7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UwpWindowsProviderSample", "Samples\UwpWindowsProviderSample\UwpWindowsProviderSample.csproj", "{C60C02DF-F44C-4449-A1D4-C2DC3A7959B9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManualGraphRequestSample", "Samples\ManualGraphRequestSample\ManualGraphRequestSample.csproj", "{192CC7FD-408D-4B0B-9032-AD06C7BE46C6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfNetCoreMsalProviderSample", "Samples\WpfNetCoreMsalProviderSample\WpfNetCoreMsalProviderSample.csproj", "{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
CI|Any CPU = CI|Any CPU
@ -540,6 +542,46 @@ Global
{192CC7FD-408D-4B0B-9032-AD06C7BE46C6}.Release|x86.ActiveCfg = Release|x86
{192CC7FD-408D-4B0B-9032-AD06C7BE46C6}.Release|x86.Build.0 = Release|x86
{192CC7FD-408D-4B0B-9032-AD06C7BE46C6}.Release|x86.Deploy.0 = Release|x86
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|Any CPU.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|Any CPU.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|ARM.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|ARM.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|ARM64.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|ARM64.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|x64.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|x64.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|x86.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.CI|x86.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|ARM.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|ARM.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|ARM64.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|x64.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|x64.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|x86.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Debug|x86.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|Any CPU.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|Any CPU.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|ARM.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|ARM.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|ARM64.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|ARM64.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|x64.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|x64.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|x86.ActiveCfg = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Native|x86.Build.0 = Debug|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|Any CPU.Build.0 = Release|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|ARM.ActiveCfg = Release|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|ARM.Build.0 = Release|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|ARM64.ActiveCfg = Release|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|ARM64.Build.0 = Release|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|x64.ActiveCfg = Release|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|x64.Build.0 = Release|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|x86.ActiveCfg = Release|Any CPU
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -550,6 +592,7 @@ Global
{EDAD72A8-498B-4645-AD1A-E5CDBDB610F7} = {022BF202-8D0D-4A6B-8A5B-92376D2EB5DA}
{C60C02DF-F44C-4449-A1D4-C2DC3A7959B9} = {022BF202-8D0D-4A6B-8A5B-92376D2EB5DA}
{192CC7FD-408D-4B0B-9032-AD06C7BE46C6} = {022BF202-8D0D-4A6B-8A5B-92376D2EB5DA}
{86AD7D3C-F03F-4FD1-8D69-AB0520805A65} = {022BF202-8D0D-4A6B-8A5B-92376D2EB5DA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {20641689-5BDE-4F6F-8889-CCDD2CC2685E}