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.

At the end of this guide, your application calls a protected API by using personal accounts. Examples are outlook.com, live.com, and others. Your application also 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

How this guide works

Shows how the sample app generated by this tutorial 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.

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

  1. Open Visual Studio, and select Create a new project.

  2. In Create a new project, choose Blank App (Universal Windows) for C# and select Next.

  3. In Configure your new project, name the app, and select Create.

  4. If prompted, in New Universal Windows Platform Project, select any version for Target and Minimum versions, and select OK.

    Minimum and Target versions

Add the Microsoft Authentication Library to your project

  1. In Visual Studio, select Tools > NuGet Package Manager > Package Manager Console.

  2. 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.

  1. 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;
    
  2. 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[] { "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.onmicrosoft.com]"
        private const string Authority = "https://login.microsoftonline.com/" + Tenant;
    
        // The MSAL Public client app
        private static IPublicClientApplication PublicClientApp;
    
        private static string MSGraphURL = "https://graph.microsoft.com/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.microsoftonline.com/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 an MsalUiRequiredException 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 retry AcquireTokenSilent 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:

  1. Sign in to the Microsoft Entra admin center as at least an Application Developer.
  2. 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.
  3. Browse to Identity > Applications > App registrations.
  4. Select New registration.
  5. 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.
  6. Under Supported account types, select Accounts in any organizational directory (Any Microsoft Entra directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox).
  7. Select Register.
  8. 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:

  1. In to the Microsoft Entra admin center, select Authentication > Add a platform, and then select Mobile and desktop applications.
  2. In the Redirect URIs section, enter https://login.microsoftonline.com/common/oauth2/nativeclient.
  3. Select Configure.

Configure API permissions for your application:

  1. Select API permissions > Add a permission.
  2. Select Microsoft Graph.
  3. Select Delegated permissions, search for User.Read, and verify that User.Read is selected.
  4. 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.

  1. Open Package.appxmanifest.

  2. 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.microsoftonline.com/common/oauth2/nativeclient") method is used. To use WithDefaultRedirectURI(), complete these steps:

  1. In MainPage.XAML.cs, replace WithRedirectUri with WithDefaultRedirectUri:

    Current code

    
    PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
        .WithAuthority(Authority)
        .WithUseCorporateNetwork(false)
        .WithRedirectUri("https://login.microsoftonline.com/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.microsoftonline.com/common")
        .WithUseCorporateNetwork(false)
        .WithDefaultRedirectUri()
        .WithLogging((level, message, containsPii) =>
         {
             Debug.WriteLine($"MSAL: {level} {message} ");
         }, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
        .Build();
    
  2. 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.

  3. 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:

Application's user interface

When you're ready to test, select Call Microsoft Graph API. Then use a Microsoft Entra organizational account or a Microsoft account, such as live.com or outlook.com, to sign in. The first time a user runs this test, the application displays a window asking the user to sign in.

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:

Access consent screen

Expected results

You see user profile information returned by the Microsoft Graph API call on the API Call Results screen:

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: