Skip to content

Commit 0699cb2

Browse files
Add project files.
1 parent 9931d40 commit 0699cb2

16 files changed

+675
-0
lines changed

Diff for: UWPWAM.sln

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.32106.194
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UWPWAM", "UWPWAM\UWPWAM.csproj", "{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|ARM = Debug|ARM
11+
Debug|ARM64 = Debug|ARM64
12+
Debug|x64 = Debug|x64
13+
Debug|x86 = Debug|x86
14+
Release|ARM = Release|ARM
15+
Release|ARM64 = Release|ARM64
16+
Release|x64 = Release|x64
17+
Release|x86 = Release|x86
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|ARM.ActiveCfg = Debug|ARM
21+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|ARM.Build.0 = Debug|ARM
22+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|ARM.Deploy.0 = Debug|ARM
23+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|ARM64.ActiveCfg = Debug|ARM64
24+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|ARM64.Build.0 = Debug|ARM64
25+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|ARM64.Deploy.0 = Debug|ARM64
26+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|x64.ActiveCfg = Debug|x64
27+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|x64.Build.0 = Debug|x64
28+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|x64.Deploy.0 = Debug|x64
29+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|x86.ActiveCfg = Debug|x86
30+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|x86.Build.0 = Debug|x86
31+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Debug|x86.Deploy.0 = Debug|x86
32+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|ARM.ActiveCfg = Release|ARM
33+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|ARM.Build.0 = Release|ARM
34+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|ARM.Deploy.0 = Release|ARM
35+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|ARM64.ActiveCfg = Release|ARM64
36+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|ARM64.Build.0 = Release|ARM64
37+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|ARM64.Deploy.0 = Release|ARM64
38+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|x64.ActiveCfg = Release|x64
39+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|x64.Build.0 = Release|x64
40+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|x64.Deploy.0 = Release|x64
41+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|x86.ActiveCfg = Release|x86
42+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|x86.Build.0 = Release|x86
43+
{0E96F032-4BFB-4EDA-8093-AAC7F5AB5432}.Release|x86.Deploy.0 = Release|x86
44+
EndGlobalSection
45+
GlobalSection(SolutionProperties) = preSolution
46+
HideSolutionNode = FALSE
47+
EndGlobalSection
48+
GlobalSection(ExtensibilityGlobals) = postSolution
49+
SolutionGuid = {6390D60F-6702-42B1-B04A-E131D4AB71FF}
50+
EndGlobalSection
51+
EndGlobal

Diff for: UWPWAM/App.xaml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<Application
2+
x:Class="UWPWAM.App"
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:local="using:UWPWAM">
6+
7+
</Application>

Diff for: UWPWAM/App.xaml.cs

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Runtime.InteropServices.WindowsRuntime;
6+
using Windows.ApplicationModel;
7+
using Windows.ApplicationModel.Activation;
8+
using Windows.Foundation;
9+
using Windows.Foundation.Collections;
10+
using Windows.UI.Xaml;
11+
using Windows.UI.Xaml.Controls;
12+
using Windows.UI.Xaml.Controls.Primitives;
13+
using Windows.UI.Xaml.Data;
14+
using Windows.UI.Xaml.Input;
15+
using Windows.UI.Xaml.Media;
16+
using Windows.UI.Xaml.Navigation;
17+
18+
namespace UWPWAM
19+
{
20+
/// <summary>
21+
/// Provides application-specific behavior to supplement the default Application class.
22+
/// </summary>
23+
sealed partial class App : Application
24+
{
25+
/// <summary>
26+
/// Initializes the singleton application object. This is the first line of authored code
27+
/// executed, and as such is the logical equivalent of main() or WinMain().
28+
/// </summary>
29+
public App()
30+
{
31+
this.InitializeComponent();
32+
this.Suspending += OnSuspending;
33+
}
34+
35+
/// <summary>
36+
/// Invoked when the application is launched normally by the end user. Other entry points
37+
/// will be used such as when the application is launched to open a specific file.
38+
/// </summary>
39+
/// <param name="e">Details about the launch request and process.</param>
40+
protected override void OnLaunched(LaunchActivatedEventArgs e)
41+
{
42+
Frame rootFrame = Window.Current.Content as Frame;
43+
44+
// Do not repeat app initialization when the Window already has content,
45+
// just ensure that the window is active
46+
if (rootFrame == null)
47+
{
48+
// Create a Frame to act as the navigation context and navigate to the first page
49+
rootFrame = new Frame();
50+
51+
rootFrame.NavigationFailed += OnNavigationFailed;
52+
53+
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
54+
{
55+
//TODO: Load state from previously suspended application
56+
}
57+
58+
// Place the frame in the current Window
59+
Window.Current.Content = rootFrame;
60+
}
61+
62+
if (e.PrelaunchActivated == false)
63+
{
64+
if (rootFrame.Content == null)
65+
{
66+
// When the navigation stack isn't restored navigate to the first page,
67+
// configuring the new page by passing required information as a navigation
68+
// parameter
69+
rootFrame.Navigate(typeof(MainPage), e.Arguments);
70+
}
71+
// Ensure the current window is active
72+
Window.Current.Activate();
73+
}
74+
}
75+
76+
/// <summary>
77+
/// Invoked when Navigation to a certain page fails
78+
/// </summary>
79+
/// <param name="sender">The Frame which failed navigation</param>
80+
/// <param name="e">Details about the navigation failure</param>
81+
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
82+
{
83+
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
84+
}
85+
86+
/// <summary>
87+
/// Invoked when application execution is being suspended. Application state is saved
88+
/// without knowing whether the application will be terminated or resumed with the contents
89+
/// of memory still intact.
90+
/// </summary>
91+
/// <param name="sender">The source of the suspend request.</param>
92+
/// <param name="e">Details about the suspend request.</param>
93+
private void OnSuspending(object sender, SuspendingEventArgs e)
94+
{
95+
var deferral = e.SuspendingOperation.GetDeferral();
96+
//TODO: Save application state and stop any background activity
97+
deferral.Complete();
98+
}
99+
}
100+
}

Diff for: UWPWAM/Assets/LockScreenLogo.scale-200.png

1.4 KB
Loading

Diff for: UWPWAM/Assets/SplashScreen.scale-200.png

7.52 KB
Loading

Diff for: UWPWAM/Assets/Square150x150Logo.scale-200.png

2.87 KB
Loading

Diff for: UWPWAM/Assets/Square44x44Logo.scale-200.png

1.61 KB
Loading
Loading

Diff for: UWPWAM/Assets/StoreLogo.png

1.42 KB
Loading

Diff for: UWPWAM/Assets/Wide310x150Logo.scale-200.png

3.13 KB
Loading

Diff for: UWPWAM/MainPage.xaml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Page
2+
x:Class="UWPWAM.MainPage"
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:local="using:UWPWAM"
6+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
8+
mc:Ignorable="d"
9+
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
10+
11+
<Grid>
12+
<StackPanel Background="Azure">
13+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
14+
<Button x:Name="CallGraphButton" Content="Call Microsoft Graph API" HorizontalAlignment="Right" Padding="5" Click="CallGraphButton_Click" Margin="5" FontFamily="Segoe Ui"/>
15+
<Button x:Name="SignOutButton" Content="Sign-Out" HorizontalAlignment="Right" Padding="5" Click="SignOutButton_Click" Margin="5" Visibility="Collapsed" FontFamily="Segoe Ui"/>
16+
</StackPanel>
17+
<TextBlock Text="API Call Results" Margin="2,0,0,-5" FontFamily="Segoe Ui" />
18+
<TextBox x:Name="ResultText" TextWrapping="Wrap" MinHeight="120" Margin="5" FontFamily="Segoe Ui"/>
19+
<TextBlock Text="Token Info" Margin="2,0,0,-5" FontFamily="Segoe Ui" />
20+
<TextBox x:Name="TokenInfoText" TextWrapping="Wrap" MinHeight="70" Margin="5" FontFamily="Segoe Ui"/>
21+
</StackPanel>
22+
</Grid>
23+
</Page>

Diff for: UWPWAM/MainPage.xaml.cs

+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Runtime.InteropServices.WindowsRuntime;
6+
using Windows.Foundation;
7+
using Windows.Foundation.Collections;
8+
using Windows.UI.Xaml;
9+
using Windows.UI.Xaml.Controls;
10+
using Windows.UI.Xaml.Controls.Primitives;
11+
using Windows.UI.Xaml.Data;
12+
using Windows.UI.Xaml.Input;
13+
using Windows.UI.Xaml.Media;
14+
using Windows.UI.Xaml.Navigation;
15+
using Microsoft.Identity.Client;
16+
using Microsoft.Graph;
17+
using System.Diagnostics;
18+
using System.Threading.Tasks;
19+
using System.Net.Http.Headers;
20+
using Windows.Security.Authentication.Web;
21+
using System.Threading;
22+
23+
24+
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
25+
26+
namespace UWPWAM
27+
{
28+
/// <summary>
29+
/// An empty page that can be used on its own or navigated to within a Frame.
30+
/// </summary>
31+
public sealed partial class MainPage : Page
32+
{
33+
34+
//Set the scope for API call to user.read
35+
private string[] scopes = new string[] { "user.read" };
36+
37+
// Below are the clientId (Application Id) of your app registration and the tenant information.
38+
// You have to replace:
39+
// - the content of ClientID with the Application Id for your app registration
40+
//private const string ClientId = "[Application Id pasted from the application registration portal]";
41+
42+
private const string ClientId = "4b0db8c2-9f26-4417-8bde-3f0e3656f8e0";
43+
44+
private const string Tenant = "common"; // Alternatively "[Enter your tenant, as obtained from the Azure portal, e.g. kko365.onmicrosoft.com]"
45+
private const string Authority = "https://login.microsoftonline.com/" + Tenant;
46+
47+
// The MSAL Public client app
48+
private static IPublicClientApplication PublicClientApp;
49+
50+
private static string MSGraphURL = "https://graph.microsoft.com/v1.0/";
51+
private static AuthenticationResult authResult;
52+
53+
public MainPage()
54+
{
55+
this.InitializeComponent();
56+
57+
// returns something like s-1-15-2-2601115387-131721061-1180486061-1362788748-631273777-3164314714-2766189824
58+
string sid = WebAuthenticationBroker.GetCurrentApplicationCallbackUri().Host;
59+
60+
// use uppercase S
61+
sid = sid.Replace('s', 'S');
62+
63+
// the redirect URI
64+
string redirectUri = $"ms-appx-web://microsoft.aad.brokerplugin/{sid}";
65+
}
66+
67+
/// <summary>
68+
/// Call AcquireTokenAsync - to acquire a token requiring user to sign in
69+
/// </summary>
70+
private async void CallGraphButton_Click(object sender, RoutedEventArgs e)
71+
{
72+
try
73+
{
74+
// Sign in user using MSAL and obtain an access token for Microsoft Graph
75+
GraphServiceClient graphClient = await SignInAndInitializeGraphServiceClient(scopes);
76+
77+
// Call the /me endpoint of Graph
78+
User graphUser = await graphClient.Me.Request().GetAsync();
79+
80+
// Go back to the UI thread to make changes to the UI
81+
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
82+
{
83+
ResultText.Text = "Display Name: " + graphUser.DisplayName + "\nBusiness Phone: " + graphUser.BusinessPhones.FirstOrDefault()
84+
+ "\nGiven Name: " + graphUser.GivenName + "\nid: " + graphUser.Id
85+
+ "\nUser Principal Name: " + graphUser.UserPrincipalName;
86+
DisplayBasicTokenInfo(authResult);
87+
this.SignOutButton.Visibility = Visibility.Visible;
88+
});
89+
}
90+
catch (MsalException msalEx)
91+
{
92+
await DisplayMessageAsync($"Error Acquiring Token:{System.Environment.NewLine}{msalEx}");
93+
}
94+
catch (Exception ex)
95+
{
96+
await DisplayMessageAsync($"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}");
97+
return;
98+
}
99+
}
100+
/// <summary>
101+
/// Signs in the user and obtains an access token for Microsoft Graph
102+
/// </summary>
103+
/// <param name="scopes"></param>
104+
/// <returns> Access Token</returns>
105+
private static async Task<string> SignInUserAndGetTokenUsingMSAL(string[] scopes)
106+
{
107+
// Initialize the MSAL library by building a public client application
108+
PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
109+
.WithAuthority(Authority)
110+
.WithBroker()
111+
.WithLogging((level, message, containsPii) =>
112+
{
113+
Debug.WriteLine($"MSAL: {level} {message} ");
114+
}, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
115+
.Build();
116+
117+
118+
IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(true);
119+
IAccount firstAccount = accounts.FirstOrDefault();
120+
121+
try
122+
{
123+
authResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
124+
.ExecuteAsync();
125+
}
126+
catch (MsalUiRequiredException ex)
127+
{
128+
// A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token
129+
Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
130+
131+
authResult = await PublicClientApp.AcquireTokenInteractive(scopes)
132+
//.WithAccount(firstAccount)
133+
.ExecuteAsync()
134+
.ConfigureAwait(true);
135+
136+
}
137+
return authResult.AccessToken;
138+
}
139+
140+
/// <summary>
141+
/// Sign in user using MSAL and obtain a token for Microsoft Graph
142+
/// </summary>
143+
/// <returns>GraphServiceClient</returns>
144+
private async static Task<GraphServiceClient> SignInAndInitializeGraphServiceClient(string[] scopes)
145+
{
146+
GraphServiceClient graphClient = new GraphServiceClient(MSGraphURL,
147+
new DelegateAuthenticationProvider(async (requestMessage) =>
148+
{
149+
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", await SignInUserAndGetTokenUsingMSAL(scopes));
150+
}));
151+
152+
return await Task.FromResult(graphClient);
153+
}
154+
155+
/// <summary>
156+
/// Sign out the current user
157+
/// </summary>
158+
private async void SignOutButton_Click(object sender, RoutedEventArgs e)
159+
{
160+
IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(false);
161+
IAccount firstAccount = accounts.FirstOrDefault();
162+
163+
try
164+
{
165+
await PublicClientApp.RemoveAsync(firstAccount).ConfigureAwait(false);
166+
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
167+
{
168+
ResultText.Text = "User has signed out";
169+
this.CallGraphButton.Visibility = Visibility.Visible;
170+
this.SignOutButton.Visibility = Visibility.Collapsed;
171+
});
172+
}
173+
catch (MsalException ex)
174+
{
175+
ResultText.Text = $"Error signing out user: {ex.Message}";
176+
}
177+
}
178+
179+
/// <summary>
180+
/// Display basic information contained in the token. Needs to be called from the UI thread.
181+
/// </summary>
182+
private void DisplayBasicTokenInfo(AuthenticationResult authResult)
183+
{
184+
TokenInfoText.Text = "";
185+
if (authResult != null)
186+
{
187+
TokenInfoText.Text += $"User Name: {authResult.Account.Username}" + Environment.NewLine;
188+
TokenInfoText.Text += $"Token Expires: {authResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
189+
}
190+
}
191+
192+
/// <summary>
193+
/// Displays a message in the ResultText. Can be called from any thread.
194+
/// </summary>
195+
private async Task DisplayMessageAsync(string message)
196+
{
197+
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
198+
() =>
199+
{
200+
ResultText.Text = message;
201+
});
202+
}
203+
}
204+
}

0 commit comments

Comments
 (0)