Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructuring package layout, removing behaviors #76

Merged
merged 23 commits into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
902a1f9
- Removed Behaviors
shweaver-MSFT Mar 9, 2021
486fd32
Update Graph SDK and change default sample auth method
shweaver-MSFT Mar 9, 2021
8557e9b
Major restructuring wip
shweaver-MSFT Mar 11, 2021
7d19b88
Restructuring completed
shweaver-MSFT Mar 11, 2021
751c7fa
Fixed GraphPresenter sample after latest changes
shweaver-MSFT Mar 11, 2021
6283857
Removed unecessary IProviderConfig interface, replaced with object
shweaver-MSFT Mar 15, 2021
aa8662a
Updated syntax in provider implementations
shweaver-MSFT Mar 15, 2021
306b9b9
Lint fix
shweaver-MSFT Mar 15, 2021
92ad9b1
Renamed packages to new-er format
shweaver-MSFT Mar 16, 2021
976ee86
Renamed UI to Controls
shweaver-MSFT Mar 16, 2021
17a4673
csproj fixes
shweaver-MSFT Mar 17, 2021
cb9c2a6
Removed Graph Auth reference and moved Msal to own package
shweaver-MSFT Mar 18, 2021
e1b24d1
Added helper method for authenticating http requests manually
shweaver-MSFT Mar 18, 2021
ee9e658
csproj adjustments and added helper method for authenticating requests
shweaver-MSFT Mar 18, 2021
0833f15
Update CommunityToolkit.Uwp.Graph.Controls/CommunityToolkit.Uwp.Graph…
shweaver-MSFT Mar 24, 2021
8d0da59
Update CommunityToolkit.Net.Graph/CommunityToolkit.Net.Graph.csproj
shweaver-MSFT Mar 24, 2021
9000daa
Update CommunityToolkit.Net.Graph/CommunityToolkit.Net.Graph.csproj
shweaver-MSFT Mar 24, 2021
07f6036
Update CommunityToolkit.Net.Authentication/CommunityToolkit.Net.Authe…
shweaver-MSFT Mar 24, 2021
50947c9
Update CommunityToolkit.Net.Authentication/CommunityToolkit.Net.Authe…
shweaver-MSFT Mar 24, 2021
32c10b8
Update SampleTest/App.xaml.cs
shweaver-MSFT Mar 24, 2021
4f215c7
Update CommunityToolkit.Net.Authentication.Msal/CommunityToolkit.Net.…
shweaver-MSFT Mar 24, 2021
25bd63c
Adjusted msalProvider constructor argument order
shweaver-MSFT Mar 24, 2021
0ce9faf
Merge branch 'shweaver/bad-behaviors' of https://github.com/windows-t…
shweaver-MSFT Mar 24, 2021
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="MSBuild.Sdk.Extras">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Title>Community Toolkit .NET Standard Auth Services</Title>
<Description>
This package includes .NET Standard authentication helpers such as:
- MsalProvider:
</Description>
<PackageTags>Community Toolkit Provider Authentication Auth Msal</PackageTags>

<!-- This is a temporary workaround for https://github.com/dotnet/sdk/issues/955 -->
<DebugType>Full</DebugType>
<Configurations>Debug;Release;CI</Configurations>
<Platforms>AnyCPU;ARM;ARM64;x64;x86</Platforms>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Identity.Client" Version="4.28.0" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="2.18.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CommunityToolkit.Net.Authentication\CommunityToolkit.Net.Authentication.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,102 +6,70 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using Microsoft.Toolkit.Graph.Extensions;

namespace Microsoft.Toolkit.Graph.Providers
namespace CommunityToolkit.Net.Authentication.Msal
{
//// TODO: Move some of this to a simple base-class for non-MSAL parts related to Provider only and properties?

/// <summary>
/// <a href="https://github.com/AzureAD/microsoft-authentication-library-for-dotnet">MSAL.NET</a> provider helper for tracking authentication state using an <see cref="IAuthenticationProvider"/> class.
/// <a href="https://github.com/AzureAD/microsoft-authentication-library-for-dotnet">MSAL.NET</a> provider helper for tracking authentication state.
/// </summary>
public class MsalProvider : IProvider
public class MsalProvider : BaseProvider
{
/// <summary>
/// Gets or sets the MSAL.NET Client used to authenticate the user.
/// </summary>
protected IPublicClientApplication Client { get; set; }

/// <summary>
/// Gets or sets the provider used by the graph to manage requests.
/// Gets the MSAL.NET Client used to authenticate the user.
/// </summary>
protected IAuthenticationProvider Provider { get; set; }

private ProviderState _state = ProviderState.Loading;

/// <inheritdoc/>
public ProviderState State
{
get
{
return _state;
}

private set
{
var current = _state;
_state = value;

StateChanged?.Invoke(this, new StateChangedEventArgs(current, _state));
}
}

/// <inheritdoc/>
public GraphServiceClient Graph { get; private set; }

/// <inheritdoc/>
public event EventHandler<StateChangedEventArgs> StateChanged;
protected IPublicClientApplication Client { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="MsalProvider"/> class. <see cref="CreateAsync"/>.
/// Gets an array of scopes to use for accessing Graph resources.
/// </summary>
private MsalProvider()
{
}
protected string[] Scopes { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="MsalProvider"/> class.
/// </summary>
/// <param name="client">Existing <see cref="IPublicClientApplication"/> instance.</param>
/// <param name="provider">Existing <see cref="IAuthenticationProvider"/> instance.</param>
/// <returns>A <see cref="Task"/> returning a <see cref="MsalProvider"/> instance.</returns>
public static async Task<MsalProvider> CreateAsync(IPublicClientApplication client, IAuthenticationProvider provider)
/// <param name="clientId">Registered ClientId.</param>
/// <param name="redirectUri">RedirectUri for auth response.</param>
/// <param name="scopes">List of Scopes to initially request.</param>
public MsalProvider(string clientId, string redirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient", string[] scopes = null)
{
//// TODO: Check all config provided

var msal = new MsalProvider
{
Client = client,
Provider = provider,
};
var client = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(AzureCloudInstance.AzurePublic, AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount)
.WithRedirectUri(redirectUri)
.WithClientName(ProviderManager.ClientName)
.WithClientVersion(Assembly.GetExecutingAssembly().GetName().Version.ToString())
.Build();

msal.Graph = new GraphServiceClient(msal);
Scopes = scopes ?? new string[] { string.Empty };

await msal.TrySilentSignInAsync();
Client = client;

return msal;
_ = TrySilentSignInAsync();
}

/// <inheritdoc/>
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
public override async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
request.AddSdkVersion();
IEnumerable<IAccount> accounts = await Client.GetAccountsAsync();
AuthenticationResult authResult;

try
{
await Provider.AuthenticateRequestAsync(request);
authResult = await Client.AcquireTokenSilent(Scopes, accounts.FirstOrDefault()).ExecuteAsync();
}
catch (Exception)
catch (MsalUiRequiredException)
{
// TODO: Catch different types of errors and try and re-auth? Should be handled by Graph Auth Providers.
// Assume we're signed-out on error?
State = ProviderState.SignedOut;
authResult = await Client.AcquireTokenInteractive(Scopes).ExecuteAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
}

return;
if (authResult != null)
{
AddSdkVersion(request);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
}

// Check state after request to see if we're now signed-in.
Expand Down Expand Up @@ -159,14 +127,14 @@ public async Task TrySilentSignInAsync()
}

/// <inheritdoc/>
public async Task LoginAsync()
public override async Task LoginAsync()
{
// Force fake request to start auth process
await AuthenticateRequestAsync(new System.Net.Http.HttpRequestMessage());
}

/// <inheritdoc/>
public async Task LogoutAsync()
public override async Task LogoutAsync()
{
// Forcibly remove each user.
foreach (var user in await Client.GetAccountsAsync())
Expand Down
68 changes: 68 additions & 0 deletions CommunityToolkit.Net.Authentication/BaseProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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.Net.Http;
using System.Threading.Tasks;
using CommunityToolkit.Net.Authentication.Extensions;

namespace CommunityToolkit.Net.Authentication
{
/// <summary>
/// A base construct for building Graph Providers on top of.
/// </summary>
public abstract class BaseProvider : IProvider
{
private ProviderState _state;

/// <summary>
/// Gets or sets the current state of the provider.
/// </summary>
public ProviderState State
{
get => _state;
protected set
{
var oldState = _state;
var newState = value;
if (oldState != newState)
{
_state = newState;
StateChanged?.Invoke(this, new ProviderStateChangedEventArgs(oldState, newState));
}
}
}

/// <inheritdoc/>
public event EventHandler<ProviderStateChangedEventArgs> StateChanged;

/// <summary>
/// Initializes a new instance of the <see cref="BaseProvider"/> class.
/// </summary>
public BaseProvider()
{
_state = ProviderState.Loading;
}

/// <inheritdoc />
public abstract Task LoginAsync();

/// <inheritdoc />
public abstract Task LogoutAsync();

/// <inheritdoc />
public abstract Task AuthenticateRequestAsync(HttpRequestMessage request);

/// <summary>
/// Append the Sdk version to the request headers.
/// </summary>
/// <param name="request">
/// The request to append the header to.
/// </param>
protected void AddSdkVersion(HttpRequestMessage request)
{
request.AddSdkVersion();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="MSBuild.Sdk.Extras">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Title>Community Toolkit .NET Standard Auth Services</Title>
<Description>
This package includes .NET Standard authentication helpers such as:
- BaseProvider:
- IProvider:
- MockPRovider:
- MsalProvider:
- ProviderManager:
- ProviderManagerChangedState:
- ProviderState:
- ProviderStateChangedEventArgs:
- ProviderUpdatedEventArgs:
</Description>
<PackageTags>Community Toolkit Provider Authentication Auth</PackageTags>

<!-- This is a temporary workaround for https://github.com/dotnet/sdk/issues/955 -->
<DebugType>Full</DebugType>
<Configurations>Debug;Release;CI</Configurations>
<Platforms>AnyCPU;ARM;ARM64;x64;x86</Platforms>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,24 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Toolkit.Graph.Extensions
namespace CommunityToolkit.Net.Authentication.Extensions
{
/// <summary>
/// Helpers for Graph related HTTP Headers.
/// </summary>
internal static class HttpRequestMessageExtensions
public static class HttpRequestMessageExtensions
{
private const string SdkVersion = "SdkVersion";
private const string LibraryName = "wct";
private const string Bearer = "Bearer";
private const string MockGraphToken = "{token:https://graph.microsoft.com/}";

public static void AddSdkVersion(this HttpRequestMessage request)
internal static void AddSdkVersion(this HttpRequestMessage request)
{
if (request == null || request.Headers == null)
{
Expand All @@ -39,11 +37,26 @@ public static void AddSdkVersion(this HttpRequestMessage request)
}
}

public static void AddMockProviderToken(this HttpRequestMessage request)
internal static void AddMockProviderToken(this HttpRequestMessage request)
{
request
.Headers
.Authorization = new AuthenticationHeaderValue(Bearer, MockGraphToken);
}

/// <summary>
/// Helper method for authenticating an http request using the GlobalProvider instance.
/// </summary>
/// <param name="request">The request to authenticate.</param>
/// <returns>A task upon completion.</returns>
public static async Task AuthenticateAsync(this HttpRequestMessage request)
{
if (ProviderManager.Instance.GlobalProvider == null)
{
throw new InvalidOperationException("The request cannot be authenticated. The GlobalProvider is null.");
}

await ProviderManager.Instance.GlobalProvider.AuthenticateRequestAsync(request);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,32 @@
// See the LICENSE file in the project root for more information.

using System;
using System.ComponentModel;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.Graph;

namespace Microsoft.Toolkit.Graph.Providers
namespace CommunityToolkit.Net.Authentication
{
/// <summary>
/// <see cref="IAuthenticationProvider"/> helper wrapper to expose more states around the authentication process for graph controls.
/// Authentication provider to expose more states around the authentication process for graph controls.
/// </summary>
public interface IProvider : IAuthenticationProvider
public interface IProvider
{
/// <summary>
/// Gets the current login state of the provider.
/// </summary>
ProviderState State { get; }

/// <summary>
/// Gets the <see cref="GraphServiceClient"/> object to access the Microsoft Graph APIs.
/// Event called when the login <see cref="State"/> changes.
/// </summary>
GraphServiceClient Graph { get; }
event EventHandler<ProviderStateChangedEventArgs> StateChanged;

/// <summary>
/// Event called when the login <see cref="State"/> changes.
/// Authenticate an outgoing request.
/// </summary>
event EventHandler<StateChangedEventArgs> StateChanged;
/// <param name="request">The request to authenticate.</param>
/// <returns>A task upon completion.</returns>
Task AuthenticateRequestAsync(HttpRequestMessage request);

/// <summary>
/// Login the user.
Expand Down
Loading