Skip to content

Commit

Permalink
Merge pull request #192 from MartinZikmund/feature/msal
Browse files Browse the repository at this point in the history
MSAL
  • Loading branch information
MartinZikmund authored Nov 28, 2023
2 parents 0ba6ff2 + 869e8d2 commit 0a81354
Show file tree
Hide file tree
Showing 24 changed files with 387 additions and 245 deletions.
4 changes: 2 additions & 2 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0-rc.2.23479.6" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0-rc.2.23479.6" />
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="2.31.0" />
<PackageVersion Include="Microsoft.TypeScript.MSBuild" Version="5.2.2" />
<PackageVersion Include="Moq" Version="4.20.69" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down Expand Up @@ -50,7 +51,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>

<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="4.8.0-3.final">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
Expand All @@ -59,7 +59,6 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>

<PackageVersion Include="Refit" Version="7.0.0" />
<PackageVersion Include="Refit.Newtonsoft.Json" Version="7.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
Expand Down Expand Up @@ -103,6 +102,7 @@
<PackageVersion Include="Uno.Wasm.Bootstrap.DevServer" Version="8.0.3" />
<PackageVersion Include="Uno.Wasm.Bootstrap.Server" Version="8.0.4" />
<PackageVersion Include="Uno.WinUI" Version="5.1.0-dev.590" />
<PackageVersion Include="Uno.WinUI.MSAL" Version="5.1.0-dev.590" />
<PackageVersion Include="Uno.WinUI.Lottie" Version="5.1.0-dev.590" />
<PackageVersion Include="Uno.WinUI.DevServer" Version="5.1.0-dev.590" />
<PackageVersion Include="Uno.WinUI.Skia.Gtk" Version="5.1.0-dev.590" />
Expand Down
59 changes: 0 additions & 59 deletions src/MZikmund.sln
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MZikmund.Skia.Gtk", "app\MZ
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MZikmund.Skia.Linux.FrameBuffer", "app\MZikmund.Skia.Linux.FrameBuffer\MZikmund.Skia.Linux.FrameBuffer.csproj", "{0EBB04BE-4BB8-4F44-AA52-04B1967FCCEC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MZikmund.Skia.WPF", "app\MZikmund.Skia.WPF\MZikmund.Skia.WPF.csproj", "{CAD5D4B9-FB38-4997-A9A8-10B89282518E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MZikmund.Wasm", "app\MZikmund.Wasm\MZikmund.Wasm.csproj", "{DBCC4BA1-A25D-4ECA-B599-CE5B14E74B67}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MZikmund.Windows", "app\MZikmund.Windows\MZikmund.Windows.csproj", "{6A207857-8DAD-47D3-AAEB-CEF69C468841}"
Expand Down Expand Up @@ -760,62 +758,6 @@ Global
{0EBB04BE-4BB8-4F44-AA52-04B1967FCCEC}.Release|x64.Build.0 = Release|Any CPU
{0EBB04BE-4BB8-4F44-AA52-04B1967FCCEC}.Release|x86.ActiveCfg = Release|Any CPU
{0EBB04BE-4BB8-4F44-AA52-04B1967FCCEC}.Release|x86.Build.0 = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|ARM.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|ARM64.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|ARM64.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|iPhone.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|x64.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|x64.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|x86.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.AppStore|x86.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|ARM.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|ARM64.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|iPhone.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|x64.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|x64.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|x86.ActiveCfg = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Debug|x86.Build.0 = Debug|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|Any CPU.Build.0 = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|ARM.ActiveCfg = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|ARM.Build.0 = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|ARM64.ActiveCfg = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|ARM64.Build.0 = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|iPhone.ActiveCfg = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|iPhone.Build.0 = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|x64.ActiveCfg = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|x64.Build.0 = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|x86.ActiveCfg = Release|Any CPU
{CAD5D4B9-FB38-4997-A9A8-10B89282518E}.Release|x86.Build.0 = Release|Any CPU
{DBCC4BA1-A25D-4ECA-B599-CE5B14E74B67}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{DBCC4BA1-A25D-4ECA-B599-CE5B14E74B67}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{DBCC4BA1-A25D-4ECA-B599-CE5B14E74B67}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -1114,7 +1056,6 @@ Global
{0D99E167-C1F3-4996-875B-9E9EB969B722} = {143CCBE2-8344-4BC0-9812-FA6B3EA703EA}
{BA5F6CFD-EEF6-467A-A99B-E5001DB1259F} = {143CCBE2-8344-4BC0-9812-FA6B3EA703EA}
{0EBB04BE-4BB8-4F44-AA52-04B1967FCCEC} = {143CCBE2-8344-4BC0-9812-FA6B3EA703EA}
{CAD5D4B9-FB38-4997-A9A8-10B89282518E} = {143CCBE2-8344-4BC0-9812-FA6B3EA703EA}
{DBCC4BA1-A25D-4ECA-B599-CE5B14E74B67} = {143CCBE2-8344-4BC0-9812-FA6B3EA703EA}
{6A207857-8DAD-47D3-AAEB-CEF69C468841} = {143CCBE2-8344-4BC0-9812-FA6B3EA703EA}
{C2E30D34-5B71-432F-B5D5-69B88B5AF5DB} = {6AD11812-0C83-402E-83D6-6D46B2CB7AF5}
Expand Down
4 changes: 2 additions & 2 deletions src/app/MZikmund.Windows/MZikmund.Windows.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
Expand All @@ -11,7 +11,7 @@
<EnableMsixTooling>true</EnableMsixTooling>

<!-- Bundles the WinAppSDK binaries -->
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<!--<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>-->

<!-- <SelfContained>true</SelfContained> -->
</PropertyGroup>
Expand Down
3 changes: 2 additions & 1 deletion src/app/MZikmund/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ private static void ConfigureServices(IServiceCollection services)
services.AddScoped<ILoadingIndicator, LoadingIndicator>();
services.AddScoped<IDialogService, DialogService>();
services.AddScoped<IWindowShellProvider, WindowShellProvider>();
services.AddSingleton<IUserService, UserService>();
services.AddSingleton(provider =>
{
var configuration = provider.GetRequiredService<IOptions<AppConfig>>();
Expand All @@ -144,7 +145,7 @@ async Task<string> GetTokenAsync(HttpRequestMessage message, CancellationToken c
{
//TODO: Move somewhere more appropriate and integrate refresh token support
var userService = Ioc.Default.GetRequiredService<IUserService>();
if (!userService.IsLoggedIn)
if (!userService.IsLoggedIn || userService.NeedsRefresh)
{
await userService.AuthenticateAsync();
}
Expand Down
3 changes: 3 additions & 0 deletions src/app/MZikmund/MZikmund.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@

<ItemGroup>
<PackageReference Include="Humanizer" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" />
<PackageReference Include="PropertyChanged.Fody">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Uno.WinUI" />
<PackageReference Include="Uno.WinUI.MSAL" />
<PackageReference Include="Uno.Resizetizer" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="Uno.Extensions.Configuration" />
Expand All @@ -27,6 +29,7 @@
<PackageReference Include="Uno.Extensions.Hosting.WinUI" />
<PackageReference Include="Uno.Extensions.Localization.WinUI" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="Microsoft.Identity.Client" />
</ItemGroup>


Expand Down
16 changes: 16 additions & 0 deletions src/app/MZikmund/Services/Account/AuthenticationConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace MZikmund.Services.Account;

public static class AuthenticationConstants
{
public const string ApplicationId = "7e13557a-4799-46b8-9e2b-0f31c41a051e";

public const string TenantId = "4e973842-1a98-40ec-9542-3c2019f0fb8e";

public static string[] DefaultScopes { get; } = new string[]
{
"api://862d5839-f30f-41a9-ab6f-ff7eef19342c/access_as_user",
"user.read",
"profile",
"offline_access"
};
}
12 changes: 12 additions & 0 deletions src/app/MZikmund/Services/Account/AuthenticationInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace MZikmund.Services.Account;

public class AuthenticationInfo
{
public required string DisplayName { get; init; }

public required DateTimeOffset ExpiresOn { get; init; }

public required string Token { get; init; }

public required string UserId { get; init; }
}
4 changes: 4 additions & 0 deletions src/app/MZikmund/Services/Account/IUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ public interface IUserService
{
bool IsLoggedIn { get; }

bool NeedsRefresh { get; }

string? UserName { get; }

string? AccessToken { get; }

Task AuthenticateAsync();
Expand Down
129 changes: 129 additions & 0 deletions src/app/MZikmund/Services/Account/UserService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using Microsoft.Identity.Client;
using Uno.UI.MSAL;
using MZikmund.Services.Preferences;
using Microsoft.Identity.Client.Extensions.Msal;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace MZikmund.Services.Account;

public class UserService : IUserService
{
private IPublicClientApplication? _identityClient;
private AuthenticationInfo? _authenticationInfo;

public UserService()
{
}

public bool IsLoggedIn => _authenticationInfo != null;

public string? UserName => _authenticationInfo?.DisplayName;

public bool NeedsRefresh => _authenticationInfo?.ExpiresOn < DateTimeOffset.UtcNow.AddMinutes(-5);

public string? AccessToken => _authenticationInfo?.Token;

public async Task AuthenticateAsync()
{
if (IsLoggedIn && !NeedsRefresh)
{
return;
}

await EnsureIdentityClientAsync();

var accounts = await _identityClient.GetAccountsAsync();
AuthenticationResult? result = null;
bool tryInteractiveLogin = false;

try
{
result = await _identityClient
.AcquireTokenSilent(AuthenticationConstants.DefaultScopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
catch (MsalUiRequiredException)
{
tryInteractiveLogin = true;
}
catch (Exception ex)
{
Debug.WriteLine($"MSAL Silent Error: {ex.Message}");
}

if (tryInteractiveLogin)
{
try
{
result = await _identityClient
.AcquireTokenInteractive(AuthenticationConstants.DefaultScopes)
.ExecuteAsync();
}
catch (Exception ex)
{
Debug.WriteLine($"MSAL Interactive Error: {ex.Message}");
}
}

_authenticationInfo = new AuthenticationInfo
{
DisplayName = result?.Account?.Username ?? "",
ExpiresOn = result?.ExpiresOn ?? DateTimeOffset.MinValue,
Token = result?.AccessToken ?? "",
UserId = result?.Account?.Username ?? ""
};
}

[MemberNotNull(nameof(_identityClient))]
private async Task EnsureIdentityClientAsync()
{
if (_identityClient == null)
{
#if __ANDROID__
_identityClient = PublicClientApplicationBuilder
.Create(AuthenticationConstants.ApplicationId)
.WithAuthority(AzureCloudInstance.AzurePublic, AuthenticationConstants.TenantId)
.WithRedirectUri($"msal{AuthenticationConstants.ApplicationId}://auth")
.WithParentActivityOrWindow(() => ContextHelper.Current)
.Build();

await Task.CompletedTask;
#elif __IOS__
_identityClient = PublicClientApplicationBuilder
.Create(AuthenticationConstants.ApplicationId)
.WithAuthority(AzureCloudInstance.AzurePublic, AuthenticationConstants.TenantId)
.WithIosKeychainSecurityGroup("com.microsoft.adalcache")
.WithRedirectUri($"msal{AuthenticationConstants.ApplicationId}://auth")
.Build();

await Task.CompletedTask;
#else
_identityClient = PublicClientApplicationBuilder
.Create(AuthenticationConstants.ApplicationId)
.WithAuthority(AzureCloudInstance.AzurePublic, AuthenticationConstants.TenantId)
.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
.WithUnoHelpers()
.Build();

await AttachTokenCacheAsync();
#endif
}
}

#if !__ANDROID__ && !__IOS__
private async Task AttachTokenCacheAsync()
{
#if !HAS_UNO
// Cache configuration and hook-up to public application. Refer to https://github.com/AzureAD/microsoft-authentication-extensions-for-dotnet/wiki/Cross-platform-Token-Cache#configuring-the-token-cache
var storageProperties = new StorageCreationPropertiesBuilder("msal.cache", ApplicationData.Current.LocalFolder.Path)
.Build();

var msalcachehelper = await MsalCacheHelper.CreateAsync(storageProperties);
msalcachehelper.RegisterCache(_identityClient!.UserTokenCache);
#else
await Task.CompletedTask;
#endif
}
#endif
}
Loading

0 comments on commit 0a81354

Please sign in to comment.