diff --git a/MAUI/MauiAppB2C/App.xaml b/MAUI/MauiAppB2C/App.xaml
new file mode 100644
index 0000000..f82e11f
--- /dev/null
+++ b/MAUI/MauiAppB2C/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppB2C/App.xaml.cs b/MAUI/MauiAppB2C/App.xaml.cs
new file mode 100644
index 0000000..a5ccd60
--- /dev/null
+++ b/MAUI/MauiAppB2C/App.xaml.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+namespace MauiB2C;
+
+public partial class App : Application
+{
+ public App()
+ {
+ InitializeComponent();
+
+ MainPage = new AppShell();
+ }
+}
diff --git a/MAUI/MauiAppB2C/AppShell.xaml b/MAUI/MauiAppB2C/AppShell.xaml
new file mode 100644
index 0000000..dd6266e
--- /dev/null
+++ b/MAUI/MauiAppB2C/AppShell.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/MAUI/MauiAppB2C/AppShell.xaml.cs b/MAUI/MauiAppB2C/AppShell.xaml.cs
new file mode 100644
index 0000000..1c2e3a5
--- /dev/null
+++ b/MAUI/MauiAppB2C/AppShell.xaml.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+namespace MauiB2C;
+
+public partial class AppShell : Shell
+{
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/MAUI/MauiAppB2C/MSALClient/B2CConstants.cs b/MAUI/MauiAppB2C/MSALClient/B2CConstants.cs
new file mode 100644
index 0000000..2c25dce
--- /dev/null
+++ b/MAUI/MauiAppB2C/MSALClient/B2CConstants.cs
@@ -0,0 +1,19 @@
+namespace MauiB2C.MSALClient
+{
+ public static class B2CConstants
+ {
+ // Azure AD B2C Coordinates
+ public const string Tenant = "fabrikamb2c.onmicrosoft.com";
+ public const string AzureADB2CHostname = "fabrikamb2c.b2clogin.com";
+ public const string ClientID = "e5737214-6372-472c-a85a-68e8fbe6cf3c";
+ public static readonly string RedirectUri = $"msal{ClientID}://auth";
+ public const string PolicySignUpSignIn = "b2c_1_susi";
+
+ public static readonly string[] Scopes = { "https://fabrikamb2c.onmicrosoft.com/helloapi/demo.read" };
+
+ public static readonly string AuthorityBase = $"https://{AzureADB2CHostname}/tfp/{Tenant}/";
+ public static readonly string AuthoritySignInSignUp = $"{AuthorityBase}{PolicySignUpSignIn}";
+
+ public const string IOSKeyChainGroup = "com.microsoft.adalcache";
+ }
+}
diff --git a/MAUI/MauiAppB2C/MSALClient/PCAWrapperB2C.cs b/MAUI/MauiAppB2C/MSALClient/PCAWrapperB2C.cs
new file mode 100644
index 0000000..d2c68b2
--- /dev/null
+++ b/MAUI/MauiAppB2C/MSALClient/PCAWrapperB2C.cs
@@ -0,0 +1,117 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+using Microsoft.Identity.Client;
+using Microsoft.IdentityModel.Abstractions;
+
+namespace MauiB2C.MSALClient
+{
+ ///
+ /// This is a wrapper for PublicClientApplication. It is singleton.
+ ///
+ public class PCAWrapperB2C
+ {
+ ///
+ /// This is the singleton used by ux. Since PCAWrapper constructor does not have perf or memory issue, it is instantiated directly.
+ ///
+ public static PCAWrapperB2C Instance { get; private set; } = new PCAWrapperB2C();
+
+ ///
+ /// Instance of PublicClientApplication. It is provided, if App wants more customization.
+ ///
+ internal IPublicClientApplication PCA { get; }
+
+ // private constructor for singleton
+ private PCAWrapperB2C()
+ {
+ // Create PCA once. Make sure that all the config parameters below are passed
+ PCA = PublicClientApplicationBuilder
+ .Create(B2CConstants.ClientID)
+ .WithExperimentalFeatures() // this is for upcoming logger
+ .WithLogging(_logger)
+ .WithB2CAuthority(B2CConstants.AuthoritySignInSignUp)
+ .WithIosKeychainSecurityGroup(B2CConstants.IOSKeyChainGroup)
+ .WithRedirectUri(B2CConstants.RedirectUri)
+ .Build();
+ }
+
+ ///
+ /// Acquire the token silently
+ ///
+ /// desired scopes
+ /// Authentication result
+ public async Task AcquireTokenSilentAsync(string[] scopes)
+ {
+ // Get accounts by policy
+ IEnumerable accounts = await PCA.GetAccountsAsync(B2CConstants.PolicySignUpSignIn).ConfigureAwait(false);
+
+ AuthenticationResult authResult = await PCA.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
+ .WithB2CAuthority(B2CConstants.AuthoritySignInSignUp)
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+
+ return authResult;
+ }
+
+ ///
+ /// Perform the interactive acquisition of the token for the given scope
+ ///
+ /// desired scopes
+ ///
+ internal async Task AcquireTokenInteractiveAsync(string[] scopes)
+ {
+ return await PCA.AcquireTokenInteractive(B2CConstants.Scopes)
+ .WithParentActivityOrWindow(PlatformConfig.Instance.ParentWindow)
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// It will sign out the user.
+ ///
+ ///
+ internal async Task SignOutAsync()
+ {
+ var accounts = await PCA.GetAccountsAsync().ConfigureAwait(false);
+ foreach (var acct in accounts)
+ {
+ await PCA.RemoveAsync(acct).ConfigureAwait(false);
+ }
+ }
+
+ // Custom logger for sample
+ private MyLogger _logger = new MyLogger();
+
+ // Custom logger class
+ private class MyLogger : IIdentityLogger
+ {
+ ///
+ /// Checks if log is enabled or not based on the Entry level
+ ///
+ ///
+ ///
+ public bool IsEnabled(EventLogLevel eventLogLevel)
+ {
+ //Try to pull the log level from an environment variable
+ var msalEnvLogLevel = Environment.GetEnvironmentVariable("MSAL_LOG_LEVEL");
+
+ EventLogLevel envLogLevel = EventLogLevel.Informational;
+ Enum.TryParse(msalEnvLogLevel, out envLogLevel);
+
+ return envLogLevel <= eventLogLevel;
+ }
+
+ ///
+ /// Log to console for demo purpose
+ ///
+ /// Log Entry values
+ public void Log(LogEntry entry)
+ {
+ Console.WriteLine(entry.Message);
+ }
+ }
+
+ }
+}
diff --git a/MAUI/MauiAppB2C/MSALClient/PlatformConfig.cs b/MAUI/MauiAppB2C/MSALClient/PlatformConfig.cs
new file mode 100644
index 0000000..395179e
--- /dev/null
+++ b/MAUI/MauiAppB2C/MSALClient/PlatformConfig.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MauiB2C.MSALClient
+{
+ ///
+ /// Platform specific configuration.
+ ///
+ public class PlatformConfig
+ {
+ ///
+ /// Instance to store data
+ ///
+ public static PlatformConfig Instance { get; } = new PlatformConfig();
+
+ ///
+ /// Platform specific Redirect URI
+ ///
+ public string RedirectUri { get; set; } = $"msal{B2CConstants.ClientID}://auth";
+
+ ///
+ /// Platform specific parent window
+ ///
+ public object ParentWindow { get; set; }
+
+ // private constructor to ensure singleton
+ private PlatformConfig()
+ {
+ }
+ }
+}
diff --git a/MAUI/MauiAppB2C/MainPage.xaml b/MAUI/MauiAppB2C/MainPage.xaml
new file mode 100644
index 0000000..a43f328
--- /dev/null
+++ b/MAUI/MauiAppB2C/MainPage.xaml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppB2C/MainPage.xaml.cs b/MAUI/MauiAppB2C/MainPage.xaml.cs
new file mode 100644
index 0000000..78cfb1b
--- /dev/null
+++ b/MAUI/MauiAppB2C/MainPage.xaml.cs
@@ -0,0 +1,78 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using MauiB2C.MSALClient;
+using Microsoft.Identity.Client;
+using System.Text;
+
+namespace MauiB2C;
+
+public partial class MainPage : ContentPage
+{
+ public MainPage()
+ {
+ InitializeComponent();
+ }
+
+ private async void OnSignInClicked(object sender, EventArgs e)
+ {
+ try
+ {
+ // First attempt silent login, which checks the cache for an existing valid token.
+ // If this is very first time or user has signed out, it will throw MsalUiRequiredException
+ AuthenticationResult result = await PCAWrapperB2C.Instance.AcquireTokenSilentAsync(B2CConstants.Scopes).ConfigureAwait(false);
+
+ string claims = GetClaims(result);
+
+ // show the claims
+ await ShowMessage("AcquireTokenTokenSilent call Claims", claims).ConfigureAwait(false);
+ }
+ catch (MsalUiRequiredException)
+ {
+ // This executes UI interaction to obtain token
+ AuthenticationResult result = await PCAWrapperB2C.Instance.AcquireTokenInteractiveAsync(B2CConstants.Scopes).ConfigureAwait(false);
+
+ string claims = GetClaims(result);
+
+ // show the Claims
+ await ShowMessage("AcquireTokenInteractive call Claims", claims).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ await ShowMessage("Exception in AcquireTokenTokenSilent", ex.Message).ConfigureAwait(false);
+ }
+ }
+
+ private static string GetClaims(AuthenticationResult result)
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (var claim in result.ClaimsPrincipal.Claims)
+ {
+ sb.Append("Claim Type = ");
+ sb.Append(claim.Type);
+ sb.Append(" Value = ");
+ sb.AppendLine(claim.Value);
+ }
+
+ return sb.ToString();
+ }
+
+ private async void SignOutButton_Clicked(object sender, EventArgs e)
+ {
+ _ = await PCAWrapperB2C.Instance.SignOutAsync().ContinueWith(async (t) =>
+ {
+ await ShowMessage("Signed Out", "Sign out complete").ConfigureAwait(false);
+ }).ConfigureAwait(false);
+ }
+
+ // display the message
+ private Task ShowMessage(string title, string message)
+ {
+ _ = this.Dispatcher.Dispatch(async () =>
+ {
+ await DisplayAlert(title, message, "OK").ConfigureAwait(false);
+ });
+
+ return Task.CompletedTask;
+ }
+}
+
diff --git a/MAUI/MauiAppB2C/MauiB2C.csproj b/MAUI/MauiAppB2C/MauiB2C.csproj
new file mode 100644
index 0000000..e2cbdc2
--- /dev/null
+++ b/MAUI/MauiAppB2C/MauiB2C.csproj
@@ -0,0 +1,55 @@
+
+
+
+ net6.0-android;net6.0-ios
+ $(TargetFrameworks);net6.0-windows10.0.19041.0
+
+
+ Exe
+ MauiB2C
+ true
+ true
+ enable
+
+
+ MauiB2C
+
+
+
+ com.yourcompany.UserDetailsClient
+ B2E9E493-DE4E-4B0B-9684-101705BF6DDD
+
+
+ 1.0
+ 1
+
+ 14.2
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppB2C/MauiProgram.cs b/MAUI/MauiAppB2C/MauiProgram.cs
new file mode 100644
index 0000000..804917a
--- /dev/null
+++ b/MAUI/MauiAppB2C/MauiProgram.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+namespace MauiB2C;
+
+public static class MauiProgram
+{
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+ return builder.Build();
+ }
+}
diff --git a/MAUI/MauiAppB2C/Platforms/Android/AndroidManifest.xml b/MAUI/MauiAppB2C/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..81aad13
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppB2C/Platforms/Android/MainActivity.cs b/MAUI/MauiAppB2C/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..a1fb98c
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/Android/MainActivity.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using Android.App;
+using Android.Content;
+using Android.Content.PM;
+using Android.OS;
+using MauiB2C.MSALClient;
+using Microsoft.Identity.Client;
+
+namespace MauiB2C;
+
+[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+public class MainActivity : MauiAppCompatActivity
+{
+ protected override void OnCreate(Bundle savedInstanceState)
+ {
+ base.OnCreate(savedInstanceState);
+ // configure platform specific params
+ PlatformConfig.Instance.ParentWindow = this;
+ }
+
+ protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
+ {
+ base.OnActivityResult(requestCode, resultCode, data);
+ AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data);
+ }
+}
diff --git a/MAUI/MauiAppB2C/Platforms/Android/MainApplication.cs b/MAUI/MauiAppB2C/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000..1c53660
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/Android/MainApplication.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using Android.App;
+using Android.Runtime;
+
+namespace MauiB2C;
+
+[Application]
+public class MainApplication : MauiApplication
+{
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/MAUI/MauiAppB2C/Platforms/Android/MsalActivity.cs b/MAUI/MauiAppB2C/Platforms/Android/MsalActivity.cs
new file mode 100644
index 0000000..9fd60e2
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/Android/MsalActivity.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Android.App;
+using Android.Content;
+using Android.OS;
+using Android.Runtime;
+using Android.Views;
+using Android.Widget;
+using Microsoft.Identity.Client;
+
+namespace MauiAppBasic.Platforms.Android.Resources
+{
+ [Activity(Exported =true)]
+ [IntentFilter(new[] { Intent.ActionView },
+ Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault },
+ DataHost = "auth",
+ DataScheme = "msale5737214-6372-472c-a85a-68e8fbe6cf3c")]
+ public class MsalActivity : BrowserTabActivity
+ {
+ }
+}
diff --git a/MAUI/MauiAppB2C/Platforms/Android/Resources/values/colors.xml b/MAUI/MauiAppB2C/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..5cd1604
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/MAUI/MauiAppB2C/Platforms/Windows/App.xaml b/MAUI/MauiAppB2C/Platforms/Windows/App.xaml
new file mode 100644
index 0000000..f0f3ddb
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/MAUI/MauiAppB2C/Platforms/Windows/App.xaml.cs b/MAUI/MauiAppB2C/Platforms/Windows/App.xaml.cs
new file mode 100644
index 0000000..45492fc
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,33 @@
+using MauiB2C.MSALClient;
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace MauiB2C.WinUI;
+
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+public partial class App : MauiWinUIApplication
+{
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ protected override void OnLaunched(LaunchActivatedEventArgs args)
+ {
+ base.OnLaunched(args);
+
+ var app = MauiB2C.App.Current;
+ PlatformConfig.Instance.ParentWindow = ((MauiWinUIWindow)app.Windows[0].Handler.PlatformView).WindowHandle;
+ }
+}
+
diff --git a/MAUI/MauiAppB2C/Platforms/Windows/Package.appxmanifest b/MAUI/MauiAppB2C/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..e98c6ed
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppB2C/Platforms/Windows/app.manifest b/MAUI/MauiAppB2C/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..b14da78
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/MAUI/MauiAppB2C/Platforms/iOS/AppDelegate.cs b/MAUI/MauiAppB2C/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000..69b5de2
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using Foundation;
+using MauiB2C.MSALClient;
+using UIKit;
+
+namespace MauiB2C;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/MAUI/MauiAppB2C/Platforms/iOS/Entitlements.plist b/MAUI/MauiAppB2C/Platforms/iOS/Entitlements.plist
new file mode 100644
index 0000000..6eaacbd
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/iOS/Entitlements.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ keychain-access-groups
+
+ $(AppIdentifierPrefix)com.microsoft.adalcache
+
+
+
diff --git a/MAUI/MauiAppB2C/Platforms/iOS/Info.plist b/MAUI/MauiAppB2C/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..358337b
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/MAUI/MauiAppB2C/Platforms/iOS/Program.cs b/MAUI/MauiAppB2C/Platforms/iOS/Program.cs
new file mode 100644
index 0000000..ae214a8
--- /dev/null
+++ b/MAUI/MauiAppB2C/Platforms/iOS/Program.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using ObjCRuntime;
+using UIKit;
+
+namespace MauiB2C;
+
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/MAUI/MauiAppB2C/Properties/launchSettings.json b/MAUI/MauiAppB2C/Properties/launchSettings.json
new file mode 100644
index 0000000..c16206a
--- /dev/null
+++ b/MAUI/MauiAppB2C/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppB2C/Resources/AppIcon/appicon.svg b/MAUI/MauiAppB2C/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..5f04fcf
--- /dev/null
+++ b/MAUI/MauiAppB2C/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppB2C/Resources/AppIcon/appiconfg.svg b/MAUI/MauiAppB2C/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/MAUI/MauiAppB2C/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppB2C/Resources/Fonts/OpenSans-Regular.ttf b/MAUI/MauiAppB2C/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..517d818
Binary files /dev/null and b/MAUI/MauiAppB2C/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/MAUI/MauiAppB2C/Resources/Fonts/OpenSans-Semibold.ttf b/MAUI/MauiAppB2C/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..3b2863d
Binary files /dev/null and b/MAUI/MauiAppB2C/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/MAUI/MauiAppB2C/Resources/Images/dotnet_bot.svg b/MAUI/MauiAppB2C/Resources/Images/dotnet_bot.svg
new file mode 100644
index 0000000..51b1c33
--- /dev/null
+++ b/MAUI/MauiAppB2C/Resources/Images/dotnet_bot.svg
@@ -0,0 +1,93 @@
+
diff --git a/MAUI/MauiAppB2C/Resources/Raw/AboutAssets.txt b/MAUI/MauiAppB2C/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..f22d3bf
--- /dev/null
+++ b/MAUI/MauiAppB2C/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with your package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/MAUI/MauiAppB2C/Resources/Splash/splash.svg b/MAUI/MauiAppB2C/Resources/Splash/splash.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/MAUI/MauiAppB2C/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppB2C/Resources/Styles/Colors.xaml b/MAUI/MauiAppB2C/Resources/Styles/Colors.xaml
new file mode 100644
index 0000000..d183ec4
--- /dev/null
+++ b/MAUI/MauiAppB2C/Resources/Styles/Colors.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+ #512BD4
+ #DFD8F7
+ #2B0B98
+ White
+ Black
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #F7B548
+ #FFD590
+ #FFE5B9
+ #28C2D1
+ #7BDDEF
+ #C3F2F4
+ #3E8EED
+ #72ACF1
+ #A7CBF6
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppB2C/Resources/Styles/Styles.xaml b/MAUI/MauiAppB2C/Resources/Styles/Styles.xaml
new file mode 100644
index 0000000..e7eefdb
--- /dev/null
+++ b/MAUI/MauiAppB2C/Resources/Styles/Styles.xaml
@@ -0,0 +1,384 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppBasic/App.xaml b/MAUI/MauiAppBasic/App.xaml
new file mode 100644
index 0000000..5724c44
--- /dev/null
+++ b/MAUI/MauiAppBasic/App.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppBasic/App.xaml.cs b/MAUI/MauiAppBasic/App.xaml.cs
new file mode 100644
index 0000000..4ba9549
--- /dev/null
+++ b/MAUI/MauiAppBasic/App.xaml.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace MauiAppBasic
+{
+ public partial class App : Application
+ {
+ public App()
+ {
+ InitializeComponent();
+
+ MainPage = new AppShell();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/AppShell.xaml b/MAUI/MauiAppBasic/AppShell.xaml
new file mode 100644
index 0000000..3c18337
--- /dev/null
+++ b/MAUI/MauiAppBasic/AppShell.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/MAUI/MauiAppBasic/AppShell.xaml.cs b/MAUI/MauiAppBasic/AppShell.xaml.cs
new file mode 100644
index 0000000..55a2b87
--- /dev/null
+++ b/MAUI/MauiAppBasic/AppShell.xaml.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace MauiAppBasic
+{
+ public partial class AppShell : Shell
+ {
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/MSALClient/AppConstants.cs b/MAUI/MauiAppBasic/MSALClient/AppConstants.cs
new file mode 100644
index 0000000..5bb03d3
--- /dev/null
+++ b/MAUI/MauiAppBasic/MSALClient/AppConstants.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MauiAppBasic.MSALClient
+{
+ ///
+ /// Defines constants for the app.
+ /// Please change them for your app
+ ///
+ internal class AppConstants
+ {
+ // ClientID of the application in (sample-testing.com)
+ internal const string ClientId = "4b706872-7c33-43f0-9325-55bf81d39b93"; // TODO - Replace with your client Id. And also replace in the AndroidManifest.xml
+
+ ///
+ /// Scopes defining what app can access in the graph
+ ///
+ internal static string[] Scopes = { "User.Read" };
+
+
+ }
+}
diff --git a/MAUI/MauiAppBasic/MSALClient/PCAWrapper.cs b/MAUI/MauiAppBasic/MSALClient/PCAWrapper.cs
new file mode 100644
index 0000000..a33eef6
--- /dev/null
+++ b/MAUI/MauiAppBasic/MSALClient/PCAWrapper.cs
@@ -0,0 +1,134 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using Microsoft.Identity.Client;
+using Microsoft.IdentityModel.Abstractions;
+
+namespace MauiAppBasic.MSALClient
+{
+ ///
+ /// This is a wrapper for PublicClientApplication. It is singleton.
+ ///
+ public class PCAWrapper
+ {
+ ///
+ /// This is the singleton used by Ux. Since PCAWrapper constructor does not have perf or memory issue, it is instantiated directly.
+ ///
+ public static PCAWrapper Instance { get; private set; } = new PCAWrapper();
+
+ ///
+ /// Instance of PublicClientApplication. It is provided, if App wants more customization.
+ ///
+ internal IPublicClientApplication PCA { get; }
+
+ ///
+ /// This will determine if the Interactive Authentication should be Embedded or System view
+ ///
+ internal bool UseEmbedded { get; set; } = false;
+
+ // private constructor for singleton
+ private PCAWrapper()
+ {
+ // Create PCA once. Make sure that all the config parameters below are passed
+ PCA = PublicClientApplicationBuilder
+ .Create(AppConstants.ClientId)
+ .WithExperimentalFeatures() // this is for upcoming logger
+ .WithLogging(_logger)
+ .WithRedirectUri(PlatformConfig.Instance.RedirectUri)
+ .WithIosKeychainSecurityGroup("com.microsoft.adalcache")
+ .Build();
+ }
+
+ ///
+ /// Acquire the token silently
+ ///
+ /// desired scopes
+ /// Authentication result
+ internal async Task AcquireTokenSilentAsync(string[] scopes)
+ {
+ var accts = await PCA.GetAccountsAsync().ConfigureAwait(false);
+ var acct = accts.FirstOrDefault();
+
+ var authResult = await PCA.AcquireTokenSilent(scopes, acct)
+ .ExecuteAsync().ConfigureAwait(false);
+ return authResult;
+
+ }
+
+ ///
+ /// Perform the interactive acquisition of the token for the given scope
+ ///
+ /// desired scopes
+ ///
+ internal async Task AcquireTokenInteractiveAsync(string[] scopes)
+ {
+ if (UseEmbedded)
+ {
+
+ return await PCA.AcquireTokenInteractive(scopes)
+ .WithUseEmbeddedWebView(true)
+ .WithParentActivityOrWindow(PlatformConfig.Instance.ParentWindow)
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+ }
+
+ SystemWebViewOptions systemWebViewOptions = new SystemWebViewOptions();
+#if IOS
+ // Hide the privacy prompt in iOS
+ systemWebViewOptions.iOSHidePrivacyPrompt = true;
+#endif
+
+ return await PCA.AcquireTokenInteractive(scopes)
+ .WithSystemWebViewOptions(systemWebViewOptions)
+ .WithParentActivityOrWindow(PlatformConfig.Instance.ParentWindow)
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// It will sign out the user.
+ ///
+ ///
+ internal async Task SignOutAsync()
+ {
+ var accounts = await PCA.GetAccountsAsync().ConfigureAwait(false);
+ foreach (var acct in accounts)
+ {
+ await PCA.RemoveAsync(acct).ConfigureAwait(false);
+ }
+ }
+
+ // Custom logger for sample
+ private readonly MyLogger _logger = new MyLogger();
+
+ // Custom logger class
+ private class MyLogger : IIdentityLogger
+ {
+ ///
+ /// Checks if log is enabled or not based on the Entry level
+ ///
+ ///
+ ///
+ public bool IsEnabled(EventLogLevel eventLogLevel)
+ {
+ //Try to pull the log level from an environment variable
+ var msalEnvLogLevel = Environment.GetEnvironmentVariable("MSAL_LOG_LEVEL");
+
+ EventLogLevel envLogLevel = EventLogLevel.Informational;
+ Enum.TryParse(msalEnvLogLevel, out envLogLevel);
+
+ return envLogLevel <= eventLogLevel;
+ }
+
+ ///
+ /// Log to console for demo purpose
+ ///
+ /// Log Entry values
+ public void Log(LogEntry entry)
+ {
+ Console.WriteLine(entry.Message);
+ }
+ }
+ }
+}
diff --git a/MAUI/MauiAppBasic/MSALClient/PlatformConfig.cs b/MAUI/MauiAppBasic/MSALClient/PlatformConfig.cs
new file mode 100644
index 0000000..73cd9b1
--- /dev/null
+++ b/MAUI/MauiAppBasic/MSALClient/PlatformConfig.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MauiAppBasic.MSALClient
+{
+ ///
+ /// Platform specific configuration.
+ ///
+ public class PlatformConfig
+ {
+ ///
+ /// Instance to store data
+ ///
+ public static PlatformConfig Instance { get; } = new PlatformConfig();
+
+ ///
+ /// Platform specific Redirect URI
+ ///
+ public string RedirectUri { get; set; }
+
+ ///
+ /// Platform specific parent window
+ ///
+ public object ParentWindow { get; set; }
+
+ // private constructor to ensure singleton
+ private PlatformConfig()
+ {
+ }
+ }
+}
diff --git a/MAUI/MauiAppBasic/MainPage.xaml b/MAUI/MauiAppBasic/MainPage.xaml
new file mode 100644
index 0000000..214d099
--- /dev/null
+++ b/MAUI/MauiAppBasic/MainPage.xaml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppBasic/MainPage.xaml.cs b/MAUI/MauiAppBasic/MainPage.xaml.cs
new file mode 100644
index 0000000..2af7c06
--- /dev/null
+++ b/MAUI/MauiAppBasic/MainPage.xaml.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using MauiAppBasic.MSALClient;
+using Microsoft.Identity.Client;
+
+namespace MauiAppBasic
+{
+ public partial class MainPage : ContentPage
+ {
+ public MainPage()
+ {
+ InitializeComponent();
+ }
+
+ private async void OnSignInClicked(object sender, EventArgs e)
+ {
+ try
+ {
+ PCAWrapper.Instance.UseEmbedded = this.useEmbedded.IsChecked;
+ // First attempt silent login, which checks the cache for an existing valid token.
+ // If this is very first time or user has signed out, it will throw MsalUiRequiredException
+ AuthenticationResult result = await PCAWrapper.Instance.AcquireTokenSilentAsync(AppConstants.Scopes).ConfigureAwait(false);
+
+ // call Web API to get the data
+ string data = await CallWebAPIWithToken(result).ConfigureAwait(false);
+
+ // show the data
+ await ShowMessage("AcquireTokenTokenSilent call", data).ConfigureAwait(false);
+ }
+ catch (MsalUiRequiredException)
+ {
+ // This executes UI interaction to obtain token
+ AuthenticationResult result = await PCAWrapper.Instance.AcquireTokenInteractiveAsync(AppConstants.Scopes).ConfigureAwait(false);
+
+ // call Web API to get the data
+ string data = await CallWebAPIWithToken(result).ConfigureAwait(false);
+
+ // show the data
+ await ShowMessage("AcquireTokenInteractive call", data).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ await ShowMessage("Exception in AcquireTokenTokenSilent", ex.Message).ConfigureAwait(false);
+ }
+ }
+ private async void SignOutButton_Clicked(object sender, EventArgs e)
+ {
+ _ = await PCAWrapper.Instance.SignOutAsync().ContinueWith(async (t) =>
+ {
+ await ShowMessage("Signed Out", "Sign out complete").ConfigureAwait(false);
+ }).ConfigureAwait(false);
+ }
+
+ // Call the web api. The code is left in the Ux file for easy to see.
+ private async Task CallWebAPIWithToken(AuthenticationResult authResult)
+ {
+ try
+ {
+ //get data from API
+ HttpClient client = new HttpClient();
+ // create the request
+ HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me");
+
+ // ** Add Authorization Header **
+ message.Headers.Add("Authorization", authResult.CreateAuthorizationHeader());
+
+ // send the request and return the response
+ HttpResponseMessage response = await client.SendAsync(message).ConfigureAwait(false);
+ string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ return responseString;
+ }
+ catch (Exception ex)
+ {
+ return ex.ToString();
+ }
+ }
+
+ // display the message
+ private async Task ShowMessage(string title, string message)
+ {
+ _ = this.Dispatcher.Dispatch(async () =>
+ {
+ await DisplayAlert(title, message, "OK").ConfigureAwait(false);
+ });
+ }
+
+ }
+}
diff --git a/MAUI/MauiAppBasic/MauiAppBasic.csproj b/MAUI/MauiAppBasic/MauiAppBasic.csproj
new file mode 100644
index 0000000..0e9c44c
--- /dev/null
+++ b/MAUI/MauiAppBasic/MauiAppBasic.csproj
@@ -0,0 +1,88 @@
+
+
+
+ net6.0-ios15.4;net7.0-android
+ $(TargetFrameworks);net6.0-windows10.0.19041.0
+
+
+ Exe
+ MauiAppBasic
+ true
+ true
+ enable
+
+
+ MauiAppBasic
+
+
+ com.companyname.mauiappbasic
+ 9B27AE8E-528F-425C-9CFE-39B4091D41B4
+
+
+ 1.0
+ 1
+
+ 14.2
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+
+
+
+ True
+ false
+ Automatic
+ iPhone Developer
+
+
+
+ True
+
+
+
+ manual
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Designer
+
+
+ Designer
+
+
+
+
+ Resources/Styles/Styles.xaml
+
+
+ Resources/Styles/Colors.xaml
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppBasic/MauiProgram.cs b/MAUI/MauiAppBasic/MauiProgram.cs
new file mode 100644
index 0000000..c9d927a
--- /dev/null
+++ b/MAUI/MauiAppBasic/MauiProgram.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace MauiAppBasic
+{
+ public static class MauiProgram
+ {
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+ return builder.Build();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Platforms/Android/AndroidManifest.xml b/MAUI/MauiAppBasic/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..9ff282b
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppBasic/Platforms/Android/MainActivity.cs b/MAUI/MauiAppBasic/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..30022ea
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/Android/MainActivity.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Android.App;
+using Android.Content;
+using Android.Content.PM;
+using Android.OS;
+using Android.Runtime;
+using MauiAppBasic.MSALClient;
+using Microsoft.Identity.Client;
+
+namespace MauiAppBasic
+{
+ [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+ public class MainActivity : MauiAppCompatActivity
+ {
+ private const string AndroidRedirectURI = $"msal{AppConstants.ClientId}://auth";
+
+ protected override void OnCreate(Bundle savedInstanceState)
+ {
+ base.OnCreate(savedInstanceState);
+ // configure platform specific params
+ PlatformConfig.Instance.RedirectUri = AndroidRedirectURI;
+ PlatformConfig.Instance.ParentWindow = this;
+ }
+
+ ///
+ /// This is a callback to continue with the authentication
+ /// Info about redirect URI: https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-client-application-configuration#redirect-uri
+ ///
+ /// request code
+ /// result code
+ /// intent of the actvity
+ protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
+ {
+ base.OnActivityResult(requestCode, resultCode, data);
+ AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/MAUI/MauiAppBasic/Platforms/Android/MainApplication.cs b/MAUI/MauiAppBasic/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000..51cd3fd
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/Android/MainApplication.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Android.App;
+using Android.Runtime;
+
+namespace MauiAppBasic
+{
+ [Application]
+ public class MainApplication : MauiApplication
+ {
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Platforms/Android/MsalActivity.cs b/MAUI/MauiAppBasic/Platforms/Android/MsalActivity.cs
new file mode 100644
index 0000000..3c36156
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/Android/MsalActivity.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Android.App;
+using Android.Content;
+using Android.OS;
+using Android.Runtime;
+using Android.Views;
+using Android.Widget;
+using Microsoft.Identity.Client;
+
+namespace MauiAppBasic.Platforms.Android.Resources
+{
+ [Activity(Exported =true)]
+ [IntentFilter(new[] { Intent.ActionView },
+ Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault },
+ DataHost = "auth",
+ DataScheme = "msal4b706872-7c33-43f0-9325-55bf81d39b93")]
+ public class MsalActivity : BrowserTabActivity
+ {
+ }
+}
diff --git a/MAUI/MauiAppBasic/Platforms/Android/Resources/values/colors.xml b/MAUI/MauiAppBasic/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..5cd1604
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Platforms/Windows/App.xaml b/MAUI/MauiAppBasic/Platforms/Windows/App.xaml
new file mode 100644
index 0000000..d4f1e87
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/MAUI/MauiAppBasic/Platforms/Windows/App.xaml.cs b/MAUI/MauiAppBasic/Platforms/Windows/App.xaml.cs
new file mode 100644
index 0000000..4f2c0f9
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using MauiAppBasic.MSALClient;
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace MauiAppBasic.WinUI
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ public partial class App : MauiWinUIApplication
+ {
+ private const string RedirectURIWindows = "http://localhost";
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ protected override void OnLaunched(LaunchActivatedEventArgs args)
+ {
+ base.OnLaunched(args);
+
+ // configure redirect URI for your application
+ PlatformConfig.Instance.RedirectUri = RedirectURIWindows;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Platforms/Windows/Package.appxmanifest b/MAUI/MauiAppBasic/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..e98c6ed
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppBasic/Platforms/Windows/app.manifest b/MAUI/MauiAppBasic/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..62bc468
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/MAUI/MauiAppBasic/Platforms/iOS/AppDelegate.cs b/MAUI/MauiAppBasic/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000..6a5a6a2
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Foundation;
+using MauiAppBasic.MSALClient;
+using UIKit;
+
+namespace MauiAppBasic
+{
+ [Register("AppDelegate")]
+ public class AppDelegate : MauiUIApplicationDelegate
+ {
+ private const string iOSRedirectURI = "msauth.com.companyname.mauiappbasic://auth"; // TODO - Replace with your redirectURI
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
+ {
+ // configure platform specific params
+ PlatformConfig.Instance.RedirectUri = iOSRedirectURI;
+
+ return base.FinishedLaunching(application, launchOptions);
+ }
+ }
+}
diff --git a/MAUI/MauiAppBasic/Platforms/iOS/Entitlements.plist b/MAUI/MauiAppBasic/Platforms/iOS/Entitlements.plist
new file mode 100644
index 0000000..6eaacbd
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/iOS/Entitlements.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ keychain-access-groups
+
+ $(AppIdentifierPrefix)com.microsoft.adalcache
+
+
+
diff --git a/MAUI/MauiAppBasic/Platforms/iOS/Info.plist b/MAUI/MauiAppBasic/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..358337b
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/MAUI/MauiAppBasic/Platforms/iOS/Program.cs b/MAUI/MauiAppBasic/Platforms/iOS/Program.cs
new file mode 100644
index 0000000..b1d1c46
--- /dev/null
+++ b/MAUI/MauiAppBasic/Platforms/iOS/Program.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using ObjCRuntime;
+using UIKit;
+
+namespace MauiAppBasic
+{
+ public class Program
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Properties/launchSettings.json b/MAUI/MauiAppBasic/Properties/launchSettings.json
new file mode 100644
index 0000000..c16206a
--- /dev/null
+++ b/MAUI/MauiAppBasic/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Resources/AppIcon/appicon.svg b/MAUI/MauiAppBasic/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..5f04fcf
--- /dev/null
+++ b/MAUI/MauiAppBasic/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Resources/AppIcon/appiconfg.svg b/MAUI/MauiAppBasic/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/MAUI/MauiAppBasic/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Resources/Fonts/OpenSans-Regular.ttf b/MAUI/MauiAppBasic/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..2c94413
Binary files /dev/null and b/MAUI/MauiAppBasic/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/MAUI/MauiAppBasic/Resources/Fonts/OpenSans-Semibold.ttf b/MAUI/MauiAppBasic/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..3c54fa7
Binary files /dev/null and b/MAUI/MauiAppBasic/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/MAUI/MauiAppBasic/Resources/Images/dotnet_bot.svg b/MAUI/MauiAppBasic/Resources/Images/dotnet_bot.svg
new file mode 100644
index 0000000..51b1c33
--- /dev/null
+++ b/MAUI/MauiAppBasic/Resources/Images/dotnet_bot.svg
@@ -0,0 +1,93 @@
+
diff --git a/MAUI/MauiAppBasic/Resources/Raw/AboutAssets.txt b/MAUI/MauiAppBasic/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..50b8a7b
--- /dev/null
+++ b/MAUI/MauiAppBasic/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/MAUI/MauiAppBasic/Resources/Splash/splash.svg b/MAUI/MauiAppBasic/Resources/Splash/splash.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/MAUI/MauiAppBasic/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Resources/Styles/Colors.xaml b/MAUI/MauiAppBasic/Resources/Styles/Colors.xaml
new file mode 100644
index 0000000..d183ec4
--- /dev/null
+++ b/MAUI/MauiAppBasic/Resources/Styles/Colors.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+ #512BD4
+ #DFD8F7
+ #2B0B98
+ White
+ Black
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #F7B548
+ #FFD590
+ #FFE5B9
+ #28C2D1
+ #7BDDEF
+ #C3F2F4
+ #3E8EED
+ #72ACF1
+ #A7CBF6
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppBasic/Resources/Styles/Styles.xaml b/MAUI/MauiAppBasic/Resources/Styles/Styles.xaml
new file mode 100644
index 0000000..94159e7
--- /dev/null
+++ b/MAUI/MauiAppBasic/Resources/Styles/Styles.xaml
@@ -0,0 +1,384 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppWithBroker/App.xaml b/MAUI/MauiAppWithBroker/App.xaml
new file mode 100644
index 0000000..69efe72
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppWithBroker/App.xaml.cs b/MAUI/MauiAppWithBroker/App.xaml.cs
new file mode 100644
index 0000000..fe73c0f
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/App.xaml.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace MauiAppWithBroker
+{
+ public partial class App : Application
+ {
+ public App()
+ {
+ InitializeComponent();
+
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+
+ MainPage = new AppShell();
+ }
+
+ private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ Exception ex = e.ExceptionObject as Exception;
+ System.Diagnostics.Debug.WriteLine(ex?.Message);
+ }
+ }
+}
diff --git a/MAUI/MauiAppWithBroker/AppShell.xaml b/MAUI/MauiAppWithBroker/AppShell.xaml
new file mode 100644
index 0000000..ab8c588
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/AppShell.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/MAUI/MauiAppWithBroker/AppShell.xaml.cs b/MAUI/MauiAppWithBroker/AppShell.xaml.cs
new file mode 100644
index 0000000..8de9cc5
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/AppShell.xaml.cs
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace MauiAppWithBroker
+{
+ public partial class AppShell : Shell
+ {
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/MSALClient/AppConstants.cs b/MAUI/MauiAppWithBroker/MSALClient/AppConstants.cs
new file mode 100644
index 0000000..95678a2
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/MSALClient/AppConstants.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MauiAppWithBroker.MSALClient
+{
+ internal static class AppConstants
+ {
+ // ClientID of the application in (ms sample testing)
+ internal const string ClientId = "858b4a09-dc31-45d3-83a7-2b5f024f99cd"; // TODO - Replace with your client Id. And also replace in the AndroidManifest.xml
+
+ // TenantID of the organization (ms sample testing)
+ internal const string TenantId = "7f58f645-c190-4ce5-9de4-e2b7acd2a6ab"; // TODO - Replace with your TenantID. And also replace in the AndroidManifest.xml
+
+ ///
+ /// Scopes defining what app can access in the graph
+ ///
+ internal static string[] Scopes = { "User.Read" };
+ }
+}
diff --git a/MAUI/MauiAppWithBroker/MSALClient/PCAWrapper.cs b/MAUI/MauiAppWithBroker/MSALClient/PCAWrapper.cs
new file mode 100644
index 0000000..68bba60
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/MSALClient/PCAWrapper.cs
@@ -0,0 +1,122 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Identity.Client;
+using Microsoft.IdentityModel.Abstractions;
+
+namespace MauiAppWithBroker.MSALClient
+{
+ ///
+ /// This is a wrapper for PublicClientApplication. It is singleton.
+ ///
+ public class PCAWrapper
+ {
+
+ ///
+ /// This is the singleton used by ux. Since PCAWrapper constructor does not have perf or memory issue, it is instantiated directly.
+ ///
+ public static PCAWrapper Instance { get; } = new PCAWrapper();
+
+ ///
+ /// Instance of PublicClientApplication. It is provided, if App wants more customization.
+ ///
+ internal IPublicClientApplication PCA { get; }
+
+ // private constructor for singleton
+ private PCAWrapper()
+ {
+ // Create PublicClientApplication once. Make sure that all the config parameters below are passed
+ PCA = PublicClientApplicationBuilder
+ .Create(AppConstants.ClientId)
+ .WithTenantId(AppConstants.TenantId)
+ .WithExperimentalFeatures() // this is for upcoming logger
+ .WithLogging(_logger)
+ .WithBroker()
+ .WithRedirectUri(PlatformConfig.Instance.RedirectUri)
+ .WithIosKeychainSecurityGroup("com.microsoft.adalcache")
+ .Build();
+ }
+
+ ///
+ /// Acquire the token silently
+ ///
+ /// desired scopes
+ /// Authentication result
+ internal async Task AcquireTokenSilentAsync(string[] scopes)
+ {
+ var accts = await PCA.GetAccountsAsync().ConfigureAwait(false);
+ var acct = accts.FirstOrDefault();
+
+ var silentParamBuilder = PCA.AcquireTokenSilent(scopes, acct);
+ var authResult = await silentParamBuilder
+ .ExecuteAsync().ConfigureAwait(false);
+ return authResult;
+
+ }
+
+ ///
+ /// Perform the interactive acquisition of the token for the given scope
+ ///
+ /// desired scopes
+ ///
+ internal async Task AcquireTokenInteractiveAsync(string[] scopes)
+ {
+ return await PCA.AcquireTokenInteractive(scopes)
+ .WithParentActivityOrWindow(PlatformConfig.Instance.ParentWindow)
+ .ExecuteAsync()
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// Signout may not perform the complete signout as company portal may hold
+ /// the token.
+ ///
+ ///
+ internal async Task SignOutAsync()
+ {
+ var accounts = await PCA.GetAccountsAsync().ConfigureAwait(false);
+ foreach (var acct in accounts)
+ {
+ await PCA.RemoveAsync(acct).ConfigureAwait(false);
+ }
+ }
+
+ // Custom logger for sample
+ private MyLogger _logger = new MyLogger();
+
+ // Custom logger class
+ private class MyLogger : IIdentityLogger
+ {
+ ///
+ /// Checks if log is enabled or not based on the Entry level
+ ///
+ ///
+ ///
+ public bool IsEnabled(EventLogLevel eventLogLevel)
+ {
+ //Try to pull the log level from an environment variable
+ var msalEnvLogLevel = Environment.GetEnvironmentVariable("MSAL_LOG_LEVEL");
+
+ EventLogLevel envLogLevel = EventLogLevel.Informational;
+ Enum.TryParse(msalEnvLogLevel, out envLogLevel);
+
+ return envLogLevel <= eventLogLevel;
+ }
+
+ ///
+ /// Log to console for demo purpose
+ ///
+ /// Log Entry values
+ public void Log(LogEntry entry)
+ {
+ Console.WriteLine(entry.Message);
+ }
+ }
+
+ }
+}
diff --git a/MAUI/MauiAppWithBroker/MSALClient/PlatformConfig.cs b/MAUI/MauiAppWithBroker/MSALClient/PlatformConfig.cs
new file mode 100644
index 0000000..d21847e
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/MSALClient/PlatformConfig.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MauiAppWithBroker.MSALClient
+{
+ ///
+ /// Platform specific configuration.
+ ///
+ public class PlatformConfig
+ {
+ ///
+ /// Instance to store data
+ ///
+ public static PlatformConfig Instance { get; } = new PlatformConfig();
+
+ ///
+ /// Platform specific Redirect URI
+ ///
+ public string RedirectUri { get; set; }
+
+ ///
+ /// Platform specific parent window
+ ///
+ public object ParentWindow { get; set; }
+
+ // private constructor to ensure singleton
+ private PlatformConfig()
+ {
+ }
+ }
+}
diff --git a/MAUI/MauiAppWithBroker/MainPage.xaml b/MAUI/MauiAppWithBroker/MainPage.xaml
new file mode 100644
index 0000000..44ceddc
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/MainPage.xaml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppWithBroker/MainPage.xaml.cs b/MAUI/MauiAppWithBroker/MainPage.xaml.cs
new file mode 100644
index 0000000..0ac7656
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/MainPage.xaml.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using MauiAppWithBroker.MSALClient;
+using Microsoft.Identity.Client;
+
+namespace MauiAppWithBroker
+{
+ public partial class MainPage : ContentPage
+ {
+ public MainPage()
+ {
+ InitializeComponent();
+ }
+
+ async private void OnSignInClicked(object sender, EventArgs e)
+ {
+ try
+ {
+ // First attempt silent login, which checks the cache for an existing valid token.
+ // If this is very first time or user has signed out, it will throw MsalUiRequiredException
+ AuthenticationResult result = await PCAWrapper.Instance.AcquireTokenSilentAsync(AppConstants.Scopes).ConfigureAwait(false);
+
+ // call Web API to get the data
+ string data = await CallWebAPIWithToken(result).ConfigureAwait(false);
+
+ // show the data
+ await ShowMessage("AcquireTokenTokenSilent call", data).ConfigureAwait(false);
+ }
+ catch (MsalUiRequiredException)
+ {
+ // This executes UI interaction to obtain token
+ AuthenticationResult result = await PCAWrapper.Instance.AcquireTokenInteractiveAsync(AppConstants.Scopes).ConfigureAwait(false);
+ // call Web API to get the data
+ string data = await CallWebAPIWithToken(result).ConfigureAwait(false);
+
+ // show the data
+ await ShowMessage("AcquireTokenInteractive call", data).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ await ShowMessage("Exception in AcquireTokenTokenSilent", ex.Message).ConfigureAwait(false);
+ }
+ }
+
+ private async void SignOutButton_Clicked(object sender, EventArgs e)
+ {
+ await PCAWrapper.Instance.SignOutAsync().ContinueWith(async (t) =>
+ {
+ await ShowMessage("Signed Out", "Sign out complete").ConfigureAwait(false);
+ }).ConfigureAwait(false);
+ }
+
+ // Call the web api. The code is left in the Ux file for easy to see.
+ private async Task CallWebAPIWithToken(AuthenticationResult authResult)
+ {
+ try
+ {
+ //get data from API
+ HttpClient client = new HttpClient();
+ // create the request
+ HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me");
+
+ // ** Add Authorization Header **
+ message.Headers.Add("Authorization", authResult.CreateAuthorizationHeader());
+
+ // send the request and return the response
+ HttpResponseMessage response = await client.SendAsync(message).ConfigureAwait(false);
+ string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ return responseString;
+ }
+ catch (Exception ex)
+ {
+ return ex.ToString();
+ }
+ }
+
+ // display the message
+ private async Task ShowMessage(string title, string message)
+ {
+ _ = this.Dispatcher.Dispatch(async () =>
+ {
+ await DisplayAlert(title, message, "OK").ConfigureAwait(false);
+ });
+ }
+ }
+}
diff --git a/MAUI/MauiAppWithBroker/MauiAppWithBroker.csproj b/MAUI/MauiAppWithBroker/MauiAppWithBroker.csproj
new file mode 100644
index 0000000..7793bb0
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/MauiAppWithBroker.csproj
@@ -0,0 +1,53 @@
+
+
+
+ net6.0-android;net6.0-ios
+ $(TargetFrameworks);net6.0-windows10.0.19041.0
+
+
+ Exe
+ MauiAppWithBroker
+ true
+ true
+ enable
+
+
+ MauiAppWithBroker
+
+
+ com.companyname.mauiappwithbroker
+ 9255FD0E-3CC7-41C3-A404-214D7EB72A0E
+
+
+ 1.0
+ 1
+
+ 10.0
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppWithBroker/MauiProgram.cs b/MAUI/MauiAppWithBroker/MauiProgram.cs
new file mode 100644
index 0000000..61e824d
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/MauiProgram.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using MauiAppWithBroker.MSALClient;
+using Microsoft.Maui.LifecycleEvents;
+
+namespace MauiAppWithBroker
+{
+ public static class MauiProgram
+ {
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureLifecycleEvents((events) =>
+ {
+#if WINDOWS
+ events.AddWindows((win) =>
+ {
+ // win.OnWindowCreated((w) => PlatformConfig.Instance.ParentWindow = w);
+ });
+#endif
+ })
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+ return builder.Build();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Platforms/Android/AndroidManifest.xml b/MAUI/MauiAppWithBroker/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..49c932a
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppWithBroker/Platforms/Android/MainActivity.cs b/MAUI/MauiAppWithBroker/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..add2ee0
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/Android/MainActivity.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Android.App;
+using Android.Content;
+using Android.Content.PM;
+using Android.OS;
+using Android.Runtime;
+using MauiAppWithBroker.MSALClient;
+using Microsoft.Identity.Client;
+
+namespace MauiAppWithBroker
+{
+ [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+ public class MainActivity : MauiAppCompatActivity
+ {
+ private const string AndroidRedirectURI = "msauth://com.companyname.mauiappwithbroker/EHyvOdXj4uLXJXDaOMy5lwANmp0="; // TODO - Replace with your redirectURI
+
+ protected override void OnCreate(Bundle savedInstanceState)
+ {
+ base.OnCreate(savedInstanceState);
+ // configure platform specific params
+ PlatformConfig.Instance.RedirectUri = AndroidRedirectURI;
+ PlatformConfig.Instance.ParentWindow = this;
+ }
+
+ ///
+ /// This is a callback to continue with the broker base authentication
+ /// Info abour redirect URI: https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-client-application-configuration#redirect-uri
+ ///
+ /// request code
+ /// result code
+ /// intent of the actvity
+ protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
+ {
+ base.OnActivityResult(requestCode, resultCode, data);
+ AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/MAUI/MauiAppWithBroker/Platforms/Android/MainApplication.cs b/MAUI/MauiAppWithBroker/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000..6407e66
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/Android/MainApplication.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Android.App;
+using Android.Runtime;
+
+namespace MauiAppWithBroker
+{
+ [Application]
+ public class MainApplication : MauiApplication
+ {
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Platforms/Android/Resources/values/colors.xml b/MAUI/MauiAppWithBroker/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..5cd1604
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Platforms/Windows/App.xaml b/MAUI/MauiAppWithBroker/Platforms/Windows/App.xaml
new file mode 100644
index 0000000..3bb9b98
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/MAUI/MauiAppWithBroker/Platforms/Windows/App.xaml.cs b/MAUI/MauiAppWithBroker/Platforms/Windows/App.xaml.cs
new file mode 100644
index 0000000..406e75c
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.UI.Xaml;
+using MauiAppWithBroker.MSALClient;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace MauiAppWithBroker.WinUI
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ public partial class App : MauiWinUIApplication
+ {
+ private const string RedirectURIWindows = "ms-appx-web://microsoft.aad.brokerplugin/858b4a09-dc31-45d3-83a7-2b5f024f99cd";
+
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ protected override void OnLaunched(LaunchActivatedEventArgs args)
+ {
+ base.OnLaunched(args);
+
+ // configure redirect URI for your application
+ PlatformConfig.Instance.RedirectUri = RedirectURIWindows;
+ var app = MauiAppWithBroker.App.Current;
+ PlatformConfig.Instance.ParentWindow = ((MauiWinUIWindow)app.Windows[0].Handler.PlatformView).WindowHandle;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Platforms/Windows/Package.appxmanifest b/MAUI/MauiAppWithBroker/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..e98c6ed
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiAppWithBroker/Platforms/Windows/app.manifest b/MAUI/MauiAppWithBroker/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..e83f78d
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/MAUI/MauiAppWithBroker/Platforms/iOS/AppDelegate.cs b/MAUI/MauiAppWithBroker/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000..217370e
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Foundation;
+using MauiAppWithBroker.MSALClient;
+using Microsoft.Identity.Client;
+using UIKit;
+
+namespace MauiAppWithBroker
+{
+ [Register("AppDelegate")]
+ public class AppDelegate : MauiUIApplicationDelegate
+ {
+ private const string iOSRedirectURI = "msauth.com.companyname.mauiappwithbroker://auth"; // TODO - Replace with your redirectURI
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
+ {
+ // configure platform specific params
+ PlatformConfig.Instance.RedirectUri = iOSRedirectURI;
+ PlatformConfig.Instance.ParentWindow = new UIViewController(); // iOS broker requires a view controller
+
+ return base.FinishedLaunching(application, launchOptions);
+ }
+
+ public override bool OpenUrl(UIApplication application, NSUrl url, NSDictionary options)
+ {
+ if (AuthenticationContinuationHelper.IsBrokerResponse(null))
+ {
+ // Done on different thread to allow return in no time.
+ _ = Task.Factory.StartNew(() => AuthenticationContinuationHelper.SetBrokerContinuationEventArgs(url));
+
+ return true;
+ }
+
+ else if (!AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/MAUI/MauiAppWithBroker/Platforms/iOS/Entitlements.plist b/MAUI/MauiAppWithBroker/Platforms/iOS/Entitlements.plist
new file mode 100644
index 0000000..6eaacbd
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/iOS/Entitlements.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ keychain-access-groups
+
+ $(AppIdentifierPrefix)com.microsoft.adalcache
+
+
+
diff --git a/MAUI/MauiAppWithBroker/Platforms/iOS/Info.plist b/MAUI/MauiAppWithBroker/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..a235fdf
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/iOS/Info.plist
@@ -0,0 +1,51 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ LSApplicationQueriesSchemes
+
+ msauthv2
+ msauthv3
+
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ com.companyname.mauiappwithbroker
+ CFBundleURLSchemes
+
+ msalbff27aee-5b7f-4588-821a-ed4ce373d8e2
+ msauth.com.companyname.mauiappwithbroker
+
+
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/MAUI/MauiAppWithBroker/Platforms/iOS/Program.cs b/MAUI/MauiAppWithBroker/Platforms/iOS/Program.cs
new file mode 100644
index 0000000..7cbb29c
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Platforms/iOS/Program.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using ObjCRuntime;
+using UIKit;
+
+namespace MauiAppWithBroker
+{
+ public class Program
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Properties/launchSettings.json b/MAUI/MauiAppWithBroker/Properties/launchSettings.json
new file mode 100644
index 0000000..c16206a
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Resources/AppIcon/appicon.svg b/MAUI/MauiAppWithBroker/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000..5f04fcf
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Resources/AppIcon/appiconfg.svg b/MAUI/MauiAppWithBroker/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Resources/Fonts/OpenSans-Regular.ttf b/MAUI/MauiAppWithBroker/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..2c94413
Binary files /dev/null and b/MAUI/MauiAppWithBroker/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/MAUI/MauiAppWithBroker/Resources/Fonts/OpenSans-Semibold.ttf b/MAUI/MauiAppWithBroker/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..3c54fa7
Binary files /dev/null and b/MAUI/MauiAppWithBroker/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/MAUI/MauiAppWithBroker/Resources/Images/dotnet_bot.svg b/MAUI/MauiAppWithBroker/Resources/Images/dotnet_bot.svg
new file mode 100644
index 0000000..51b1c33
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Resources/Images/dotnet_bot.svg
@@ -0,0 +1,93 @@
+
diff --git a/MAUI/MauiAppWithBroker/Resources/Raw/AboutAssets.txt b/MAUI/MauiAppWithBroker/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..50b8a7b
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/MAUI/MauiAppWithBroker/Resources/Splash/splash.svg b/MAUI/MauiAppWithBroker/Resources/Splash/splash.svg
new file mode 100644
index 0000000..62d66d7
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Resources/Styles/Colors.xaml b/MAUI/MauiAppWithBroker/Resources/Styles/Colors.xaml
new file mode 100644
index 0000000..d183ec4
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Resources/Styles/Colors.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+ #512BD4
+ #DFD8F7
+ #2B0B98
+ White
+ Black
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #F7B548
+ #FFD590
+ #FFE5B9
+ #28C2D1
+ #7BDDEF
+ #C3F2F4
+ #3E8EED
+ #72ACF1
+ #A7CBF6
+
+
\ No newline at end of file
diff --git a/MAUI/MauiAppWithBroker/Resources/Styles/Styles.xaml b/MAUI/MauiAppWithBroker/Resources/Styles/Styles.xaml
new file mode 100644
index 0000000..94159e7
--- /dev/null
+++ b/MAUI/MauiAppWithBroker/Resources/Styles/Styles.xaml
@@ -0,0 +1,384 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MAUI/MauiApps.sln b/MAUI/MauiApps.sln
new file mode 100644
index 0000000..2f011b4
--- /dev/null
+++ b/MAUI/MauiApps.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32526.322
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiAppWithBroker", "MauiAppWithBroker\MauiAppWithBroker.csproj", "{B2CDEFCB-CE8D-4527-8F68-23B1BE6FB3D2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiAppBasic", "MauiAppBasic\MauiAppBasic.csproj", "{D790B117-3C9A-4F69-BF62-8E29EEE3632D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiB2C", "MauiAppB2C\MauiB2C.csproj", "{9726A8E6-9C63-429F-B8F3-6277A528CBBF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B2CDEFCB-CE8D-4527-8F68-23B1BE6FB3D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B2CDEFCB-CE8D-4527-8F68-23B1BE6FB3D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B2CDEFCB-CE8D-4527-8F68-23B1BE6FB3D2}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {B2CDEFCB-CE8D-4527-8F68-23B1BE6FB3D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B2CDEFCB-CE8D-4527-8F68-23B1BE6FB3D2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B2CDEFCB-CE8D-4527-8F68-23B1BE6FB3D2}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {D790B117-3C9A-4F69-BF62-8E29EEE3632D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D790B117-3C9A-4F69-BF62-8E29EEE3632D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D790B117-3C9A-4F69-BF62-8E29EEE3632D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {D790B117-3C9A-4F69-BF62-8E29EEE3632D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D790B117-3C9A-4F69-BF62-8E29EEE3632D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D790B117-3C9A-4F69-BF62-8E29EEE3632D}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {9726A8E6-9C63-429F-B8F3-6277A528CBBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9726A8E6-9C63-429F-B8F3-6277A528CBBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9726A8E6-9C63-429F-B8F3-6277A528CBBF}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {9726A8E6-9C63-429F-B8F3-6277A528CBBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9726A8E6-9C63-429F-B8F3-6277A528CBBF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9726A8E6-9C63-429F-B8F3-6277A528CBBF}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A74CE8D7-324D-4496-81C4-5CA9CC411B18}
+ EndGlobalSection
+EndGlobal