Tutorial: Call the Microsoft Graph API from a Universal Windows Platform (UWP) application
In this tutorial, you build a native Universal Windows Platform (UWP) app that signs in users and gets an access token to call the Microsoft Graph API.
Your application calls work and school accounts from any company or organization that has Microsoft Entra ID.
In this tutorial:
- Create a Universal Windows Platform (UWP) project in Visual Studio
- Register the application in the Azure portal
- Add code to support user sign-in and sign-out
- Add code to call Microsoft Graph API
- Test the app
Prerequisites
- Visual Studio 2019 with the Universal Windows Platform development workload installed
How this guide works
This guide creates a sample UWP application that queries the Microsoft Graph API. For this scenario, a token is added to HTTP requests by using the Authorization header. The Microsoft Authentication Library handles token acquisitions and renewals.
MSAL.NET versions 4.61.0 and above do not provide support for Universal Windows Platform (UWP), Xamarin Android, and Xamarin iOS. We recommend you migrate your UWP applications to modern frameworks like WINUI. Read more about the deprecation in Announcing the Upcoming Deprecation of MSAL.NET for Xamarin and UWP.
NuGet packages
This guide uses the following NuGet package:
Library | Description |
---|---|
Microsoft.Identity.Client | Microsoft Authentication Library |
Microsoft.Graph | Microsoft Graph Client Library |
Set up your project
This section provides step-by-step instructions to integrate a Windows Desktop .NET application (XAML) with sign-in with Microsoft. Then the application can query web APIs that require a token, such as the Microsoft Graph API.
This guide creates an application that displays a button that queries the Microsoft Graph API and a button to sign out. It also displays text boxes that contain the results of the calls.
Tip
To see a completed version of the project you build in this tutorial, you can download it from GitHub.
Create your application
Open Visual Studio, and select Create a new project.
In Create a new project, choose Blank App (Universal Windows) for C# and select Next.
In Configure your new project, name the app, and select Create.
If prompted, in New Universal Windows Platform Project, select any version for Target and Minimum versions, and select OK.
Add the Microsoft Authentication Library to your project
In Visual Studio, select Tools > NuGet Package Manager > Package Manager Console.
Copy and paste the following commands in the Package Manager Console window:
Install-Package Microsoft.Identity.Client Install-Package Microsoft.Graph
Note
The first command installs the Microsoft Authentication Library (MSAL.NET). MSAL.NET acquires, caches, and refreshes user tokens that access APIs that are protected by the Microsoft identity platform. The second command installs Microsoft Graph .NET Client Library to authenticate requests to Microsoft Graph and make calls to the service.
Create your application's UI
Visual Studio creates MainPage.xaml as a part of your project template. Open this file, and then replace your application's Grid node with the following code:
<Grid>
<StackPanel Background="Azure">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="CallGraphButton" Content="Call Microsoft Graph API" HorizontalAlignment="Right" Padding="5" Click="CallGraphButton_Click" Margin="5" FontFamily="Segoe Ui"/>
<Button x:Name="SignOutButton" Content="Sign-Out" HorizontalAlignment="Right" Padding="5" Click="SignOutButton_Click" Margin="5" Visibility="Collapsed" FontFamily="Segoe Ui"/>
</StackPanel>
<TextBlock Text="API Call Results" Margin="2,0,0,-5" FontFamily="Segoe Ui" />
<TextBox x:Name="ResultText" TextWrapping="Wrap" MinHeight="120" Margin="5" FontFamily="Segoe Ui"/>
<TextBlock Text="Token Info" Margin="2,0,0,-5" FontFamily="Segoe Ui" />
<TextBox x:Name="TokenInfoText" TextWrapping="Wrap" MinHeight="70" Margin="5" FontFamily="Segoe Ui"/>
</StackPanel>
</Grid>
Use the Microsoft Authentication Library to get a token for the Microsoft Graph API
This section shows how to use the Microsoft Authentication Library to get a token for the Microsoft Graph API. Make changes to the MainPage.xaml.cs file.
In MainPage.xaml.cs, add the following references:
using Microsoft.Identity.Client; using Microsoft.Graph; using Microsoft.Graph.Models; using System.Diagnostics; using System.Threading.Tasks; using System.Net.Http.Headers;
Replace your
MainPage
class with the following code:public sealed partial class MainPage : Page { //Set the scope for API call to user.read private string[] scopes = new string[] { "https://microsoftgraph.chinacloudapi.cn/user.read" }; // Below are the clientId (Application Id) of your app registration and the tenant information. // You have to replace: // - the content of ClientID with the Application Id for your app registration private const string ClientId = "[Application Id pasted from the application registration portal]"; private const string Tenant = "common"; // Alternatively "[Enter your tenant, as obtained from the Azure portal, e.g. kko365.partner.onmschina.cn]" private const string Authority = "https://login.partner.microsoftonline.cn/" + Tenant; // The MSAL Public client app private static IPublicClientApplication PublicClientApp; private static string MSGraphURL = "https://microsoftgraph.chinacloudapi.cn/v1.0/"; private static AuthenticationResult authResult; public MainPage() { this.InitializeComponent(); } /// <summary> /// Call AcquireTokenAsync - to acquire a token requiring user to sign in /// </summary> private async void CallGraphButton_Click(object sender, RoutedEventArgs e) { try { // Sign in user using MSAL and obtain an access token for Microsoft Graph GraphServiceClient graphClient = await SignInAndInitializeGraphServiceClient(scopes); // Call the /me endpoint of Graph User graphUser = await graphClient.Me.GetAsync(); // Go back to the UI thread to make changes to the UI await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { ResultText.Text = "Display Name: " + graphUser.DisplayName + "\nBusiness Phone: " + graphUser.BusinessPhones.FirstOrDefault() + "\nGiven Name: " + graphUser.GivenName + "\nid: " + graphUser.Id + "\nUser Principal Name: " + graphUser.UserPrincipalName; DisplayBasicTokenInfo(authResult); this.SignOutButton.Visibility = Visibility.Visible; }); } catch (MsalException msalEx) { await DisplayMessageAsync($"Error Acquiring Token:{System.Environment.NewLine}{msalEx}"); } catch (Exception ex) { await DisplayMessageAsync($"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}"); return; } } /// <summary> /// Signs in the user and obtains an access token for Microsoft Graph /// </summary> /// <param name="scopes"></param> /// <returns> Access Token</returns> private static async Task<string> SignInUserAndGetTokenUsingMSAL(string[] scopes) { // Initialize the MSAL library by building a public client application PublicClientApp = PublicClientApplicationBuilder.Create(ClientId) .WithAuthority(Authority) .WithUseCorporateNetwork(false) .WithRedirectUri("https://login.partner.microsoftonline.cn/common/oauth2/nativeclient") .WithLogging((level, message, containsPii) => { Debug.WriteLine($"MSAL: {level} {message} "); }, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true) .Build(); // It's good practice to not do work on the UI thread, so use ConfigureAwait(false) whenever possible. IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(false); IAccount firstAccount = accounts.FirstOrDefault(); try { authResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount) .ExecuteAsync(); } catch (MsalUiRequiredException ex) { // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token Debug.WriteLine($"MsalUiRequiredException: {ex.Message}"); authResult = await PublicClientApp.AcquireTokenInteractive(scopes) .ExecuteAsync() .ConfigureAwait(false); } return authResult.AccessToken; } }
Get a user token interactively
The AcquireTokenInteractive
method results in a window that prompts users to sign in. Applications usually require users to sign in interactively the first time to access a protected resource. They might also need to sign in when a silent operation to acquire a token fails. An example is when a user's password has expired.
Get a user token silently
The AcquireTokenSilent
method handles token acquisitions and renewals without any user interaction. After AcquireTokenInteractive
runs for the first time and prompts the user for credentials, use the AcquireTokenSilent
method to request tokens for later calls. That method acquires tokens silently. The Microsoft Authentication Library handles token cache and renewal.
Eventually, the AcquireTokenSilent
method fails. Reasons for failure include a user that signed out or changed their password on another device. When the Microsoft Authentication Library detects that the issue requires an interactive action, it throws an MsalUiRequiredException
exception. Your application can handle this exception in two ways:
Your application calls
AcquireTokenInteractive
immediately. This call results in prompting the user to sign in. Normally, use this approach for online applications where there's no available offline content for the user. The sample generated by this guided setup follows the pattern. You see it in action the first time you run the sample.Because no user has used the application,
accounts.FirstOrDefault()
contains a null value, and throws anMsalUiRequiredException
exception.The code in the sample then handles the exception by calling
AcquireTokenInteractive
. This call results in prompting the user to sign in.Your application presents a visual indication to users that they need to sign in. Then they can select the right time to sign in. The application can retry
AcquireTokenSilent
later. Use this approach when users can use other application functionality without disruption. An example is when offline content is available in the application. In this case, users can decide when they want to sign in. The application can retryAcquireTokenSilent
after the network was temporarily unavailable.
Instantiate the Microsoft Graph Service Client by obtaining the token from the SignInUserAndGetTokenUsingMSAL method
In the project, create a new file named TokenProvider.cs: right-click on the project, select Add > New Item > Blank Page.
Add to the newly created file the following code:
using Microsoft.Kiota.Abstractions.Authentication;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace UWP_app_MSGraph {
public class TokenProvider : IAccessTokenProvider {
private Func<string[], Task<string>> getTokenDelegate;
private string[] scopes;
public TokenProvider(Func<string[], Task<string>> getTokenDelegate, string[] scopes) {
this.getTokenDelegate = getTokenDelegate;
this.scopes = scopes;
}
public Task<string> GetAuthorizationTokenAsync(Uri uri, Dictionary<string, object> additionalAuthenticationContext = default,
CancellationToken cancellationToken = default) {
return getTokenDelegate(scopes);
}
public AllowedHostsValidator AllowedHostsValidator { get; }
}
}
Tip
After pasting the code, make sure that the namespace in the TokenProvider.cs file matches the namespace of your project. This will allow you to more easily reference the TokenProvider
class in your project.
The TokenProvider
class defines a custom access token provider that executes the specified delegate method to get and return an access token.
Add the following new method to MainPage.xaml.cs:
/// <summary>
/// Sign in user using MSAL and obtain a token for Microsoft Graph
/// </summary>
/// <returns>GraphServiceClient</returns>
private async static Task<GraphServiceClient> SignInAndInitializeGraphServiceClient(string[] scopes)
{
var tokenProvider = new TokenProvider(SignInUserAndGetTokenUsingMSAL, scopes);
var authProvider = new BaseBearerTokenAuthenticationProvider(tokenProvider);
var graphClient = new GraphServiceClient(authProvider, MSGraphURL);
return await Task.FromResult(graphClient);
}
In this method, you're using the custom access token provider TokenProvider
to connect the SignInUserAndGetTokenUsingMSAL
method to the Microsoft Graph .NET SDK and create an authenticated client.
To use the BaseBearerTokenAuthenticationProvider
, in the MainPage.xaml.cs file, add the following reference:
using Microsoft.Kiota.Abstractions.Authentication;
More information on making a REST call against a protected API
In this sample application, the GetGraphServiceClient
method instantiates GraphServiceClient
by using an access token. Then, GraphServiceClient
is used to get the user's profile information from the me endpoint.
Add a method to sign out the user
To sign out the user, add the following method to MainPage.xaml.cs:
/// <summary>
/// Sign out the current user
/// </summary>
private async void SignOutButton_Click(object sender, RoutedEventArgs e)
{
IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(false);
IAccount firstAccount = accounts.FirstOrDefault();
try
{
await PublicClientApp.RemoveAsync(firstAccount).ConfigureAwait(false);
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
ResultText.Text = "User has signed out";
this.CallGraphButton.Visibility = Visibility.Visible;
this.SignOutButton.Visibility = Visibility.Collapsed;
});
}
catch (MsalException ex)
{
ResultText.Text = $"Error signing out user: {ex.Message}";
}
}
MSAL.NET uses asynchronous methods to acquire tokens or manipulate accounts. As such, support UI actions in the UI thread. This is the reason for the Dispatcher.RunAsync
call and the precautions to call ConfigureAwait(false)
.
More information about signing out
The SignOutButton_Click
method removes the user from the Microsoft Authentication Library user cache. This method effectively tells the Microsoft Authentication Library to forget the current user. A future request to acquire a token succeeds only if it's interactive.
The application in this sample supports a single user. The Microsoft Authentication Library supports scenarios where the user can sign in on more than one account. An example is an email application where a user has several accounts.
Display basic token information
Add the following method to MainPage.xaml.cs to display basic information about the token:
/// <summary>
/// Display basic information contained in the token. Needs to be called from the UI thread.
/// </summary>
private void DisplayBasicTokenInfo(AuthenticationResult authResult)
{
TokenInfoText.Text = "";
if (authResult != null)
{
TokenInfoText.Text += $"User Name: {authResult.Account.Username}" + Environment.NewLine;
TokenInfoText.Text += $"Token Expires: {authResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
}
}
More information
ID tokens acquired by using OpenID Connect also contain a small subset of information pertinent to the user. DisplayBasicTokenInfo
displays basic information contained in the token. This information includes the user's display name and ID. It also includes the expiration date of the token and the string that represents the access token itself. If you select the Call Microsoft Graph API button several times, you'll see that the same token was reused for later requests. You can also see the expiration date extended when the Microsoft Authentication Library decides it's time to renew the token.
Display message
Add the following new method to MainPage.xaml.cs:
/// <summary>
/// Displays a message in the ResultText. Can be called from any thread.
/// </summary>
private async Task DisplayMessageAsync(string message)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
{
ResultText.Text = message;
});
}
Register your application
Tip
Steps in this article might vary slightly based on the portal you start from.
Now, register your application:
- Sign in to the Microsoft Entra admin center as at least an Application Developer.
- If you have access to multiple tenants, use the Settings icon in the top menu to switch to the tenant in which you want to register the application from the Directories + subscriptions menu.
- Browse to Identity > Applications > App registrations.
- Select New registration.
- Enter a Name for your application, for example
UWP-App-calling-MSGraph
. Users of your app might see this name, and you can change it later. - Under Supported account types, select Accounts in any organizational directory (Any Microsoft Entra directory - Multitenant).
- Select Register.
- On the overview page, find the Application (client) ID value and copy it. Go back to Visual Studio, open MainPage.xaml.cs, and replace the value of
ClientId
with this value.
Configure authentication for your application:
- In to the Microsoft Entra admin center, select Authentication > Add a platform, and then select Mobile and desktop applications.
- In the Redirect URIs section, enter
https://login.partner.microsoftonline.cn/common/oauth2/nativeclient
. - Select Configure.
Configure API permissions for your application:
- Select API permissions > Add a permission.
- Select Microsoft Graph.
- Select Delegated permissions, search for User.Read, and verify that User.Read is selected.
- If you made any changes, select Add permissions to save them.
Enable integrated authentication on federated domains (optional)
To enable integrated Windows authentication when it's used with a federated Microsoft Entra domain, the application manifest must enable additional capabilities. Go back to your application in Visual Studio.
Open Package.appxmanifest.
Select Capabilities, and enable the following settings:
- Enterprise Authentication
- Private Networks (Client & Server)
- Shared User Certificates
Important
Integrated Windows authentication isn't configured by default for this sample. Applications that request Enterprise Authentication
or Shared User Certificates
capabilities require a higher level of verification by the Windows Store. Also, not all developers want to perform the higher level of verification. Enable this setting only if you need integrated Windows authentication with a federated Microsoft Entra domain.
Alternate approach to using WithDefaultRedirectURI()
In the current sample, the WithRedirectUri("https://login.partner.microsoftonline.cn/common/oauth2/nativeclient")
method is used. To use WithDefaultRedirectURI()
, complete these steps:
In MainPage.XAML.cs, replace
WithRedirectUri
withWithDefaultRedirectUri
:Current code
PublicClientApp = PublicClientApplicationBuilder.Create(ClientId) .WithAuthority(Authority) .WithUseCorporateNetwork(false) .WithRedirectUri("https://login.partner.microsoftonline.cn/common/oauth2/nativeclient") .WithLogging((level, message, containsPii) => { Debug.WriteLine($"MSAL: {level} {message} "); }, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true) .Build();
Updated code
PublicClientApp = PublicClientApplicationBuilder.Create(ClientId) .WithAuthority("https://login.partner.microsoftonline.cn/common") .WithUseCorporateNetwork(false) .WithDefaultRedirectUri() .WithLogging((level, message, containsPii) => { Debug.WriteLine($"MSAL: {level} {message} "); }, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true) .Build();
Find the callback URI for your app by adding the
redirectURI
field in MainPage.xaml.cs and setting a breakpoint on it:public sealed partial class MainPage : Page { ... string redirectURI = Windows.Security.Authentication.Web.WebAuthenticationBroker .GetCurrentApplicationCallbackUri().ToString(); public MainPage() { ... } ... }
Run the app, and then copy the value of
redirectUri
when the breakpoint is hit. The value should look something similar to the following value:ms-app://s-1-15-2-1352796503-54529114-405753024-3540103335-3203256200-511895534-1429095407/
You can then remove the line of code because it's required only once, to fetch the value.
In the Microsoft Entra admin center, add the returned value in RedirectUri in the Authentication pane.
Test your code
To test your application, select the F5 key to run your project in Visual Studio. Your main window appears:
When you're ready to test, select Call Microsoft Graph API. Then use a Microsoft Entra organizational account to sign in. The first time a user runs this test, the application displays a window asking the user to sign in.
Consent
The first time you sign in to your application, a consent screen appears similar to the following image. Select Yes to explicitly consent to access:
Expected results
You see user profile information returned by the Microsoft Graph API call on the API Call Results screen:
You also see basic information about the token acquired via AcquireTokenInteractive
or AcquireTokenSilent
in the Token Info box:
Property | Format | Description |
---|---|---|
Username |
user@domain.com |
The username that identifies the user. |
Token Expires |
DateTime |
The time when the token expires. The Microsoft Authentication Library extends the expiration date by renewing the token as necessary. |
More information about scopes and delegated permissions
The Microsoft Graph API requires the user.read
scope to read a user's profile. This scope is added by default in every application that's registered in the Application Registration Portal. Other APIs for Microsoft Graph and custom APIs for your back-end server might require additional scopes. For instance, the Microsoft Graph API requires the Calendars.Read
scope to list the user's calendars.
To access the user's calendars in the context of an application, add the Calendars.Read
delegated permission to the application registration information. Then add the Calendars.Read
scope to the acquireTokenSilent
call.
Users might be prompted for additional consents as you increase the number of scopes.
Known issues
Issue 1
You receive one of the following error messages when you sign in on your application on a federated Microsoft Entra domain:
- "No valid client certificate found in the request."
- "No valid certificates found in the user's certificate store."
- "Try again choosing a different authentication method."
Cause: Enterprise and certificate capabilities aren't enabled.
Solution: Follow the steps in Enable integrated authentication on federated domains (optional).
Issue 2
You enable integrated authentication on federated domains and try to use Windows Hello on a Windows 10 computer to sign in to an environment with multifactor authentication configured. The list of certificates appears. If you choose to use your PIN, the PIN window never appears.
Cause: This issue is a known limitation of the web authentication broker in UWP applications that run on Windows 10 desktops. It works fine on Windows 10 Mobile.
Workaround: Select Sign in with other options. Then select Sign in with a username and password. Select Provide your password. Then go through the phone authentication process.
Help and support
If you need help, want to report an issue, or want to learn about your support options, see Help and support for developers.
Next steps
Learn more about using the Microsoft Authentication Library (MSAL) for authorization and authentication in .NET applications: