In this section, you use MSAL to get a token for the Microsoft Graph API.
-
In the MainWindow.xaml.cs file, add the reference for MSAL to the class:
using Microsoft.Identity.Client;
-
Replace the
MainWindow
class code with the following:public partial class MainWindow : Window { //Set the API Endpoint to Graph 'me' endpoint string _graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me"; //Set the scope for API call to user.read string[] _scopes = new string[] { "user.read" }; public MainWindow() { InitializeComponent(); } /// <summary> /// Call AcquireTokenAsync - to acquire a token requiring user to sign-in /// </summary> private async void CallGraphButton_Click(object sender, RoutedEventArgs e) { AuthenticationResult authResult = null; try { authResult = await App.PublicClientApp.AcquireTokenSilentAsync(_scopes, App.PublicClientApp.Users.FirstOrDefault()); } catch (MsalUiRequiredException ex) { // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}"); try { authResult = await App.PublicClientApp.AcquireTokenAsync(_scopes); } catch (MsalException msalex) { ResultText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}"; } } catch (Exception ex) { ResultText.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}"; return; } if (authResult != null) { ResultText.Text = await GetHttpContentWithToken(_graphAPIEndpoint, authResult.AccessToken); DisplayBasicTokenInfo(authResult); this.SignOutButton.Visibility = Visibility.Visible; } } }
Calling the AcquireTokenAsync
method results in a window that prompts users to sign in. Applications usually require users to sign in interactively the first time they need to access a protected resource. They might also need to sign in when a silent operation to acquire a token fails (for example, when a user’s password is expired).
The AcquireTokenSilentAsync
method handles token acquisitions and renewals without any user interaction. After AcquireTokenAsync
is executed for the first time, AcquireTokenSilentAsync
is the usual method to use to obtain tokens that access protected resources for subsequent calls, because calls to request or renew tokens are made silently.
Eventually, the AcquireTokenSilentAsync
method will fail. Reasons for failure might be that the user has either signed out or changed their password on another device. When MSAL detects that the issue can be resolved by requiring an interactive action, it fires an MsalUiRequiredException
exception. Your application can handle this exception in two ways:
-
It can make a call against
AcquireTokenAsync
immediately. This call results in prompting the user to sign in. This pattern is usually used in online applications where there is no available offline content for the user. The sample generated by this guided setup follows this pattern, which you can see in action the first time you execute the sample.- Because no user has used the application,
PublicClientApp.Users.FirstOrDefault()
contains a null value, and anMsalUiRequiredException
exception is thrown. - The code in the sample then handles the exception by calling
AcquireTokenAsync
, which results in prompting the user to sign in.
- Because no user has used the application,
-
It can instead present a visual indication to users that an interactive sign-in is required, so that they can select the right time to sign in. Or the application can retry
AcquireTokenSilentAsync
later. This pattern is frequently used when users can use other application functionality without disruption--for example, when offline content is available in the application. In this case, users can decide when they want to sign in to either access the protected resource or refresh the outdated information. Alternatively, the application can decide to retryAcquireTokenSilentAsync
when the network is restored after having been temporarily unavailable.
Add the following new method to your MainWindow.xaml.cs
. The method is used to make a GET
request against Graph API by using an Authorize header:
/// <summary>
/// Perform an HTTP GET request to a URL using an HTTP Authorization header
/// </summary>
/// <param name="url">The URL</param>
/// <param name="token">The token</param>
/// <returns>String containing the results of the GET operation</returns>
public async Task<string> GetHttpContentWithToken(string url, string token)
{
var httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response;
try
{
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
//Add the token in Authorization header
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
return content;
}
catch (Exception ex)
{
return ex.ToString();
}
}
In this sample application, you use the GetHttpContentWithToken
method to make an HTTP GET
request against a protected resource that requires a token and then return the content to the caller. This method adds the acquired token in the HTTP Authorization header. For this sample, the resource is the Microsoft Graph API me endpoint, which displays the user's profile information.
To sign out a user, add the following method to your MainWindow.xaml.cs
file:
/// <summary>
/// Sign out the current user
/// </summary>
private void SignOutButton_Click(object sender, RoutedEventArgs e)
{
if (App.PublicClientApp.Users.Any())
{
try
{
App.PublicClientApp.Remove(App.PublicClientApp.Users.FirstOrDefault());
this.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}";
}
}
}
The SignOutButton_Click
method removes users from the MSAL user cache, which effectively tells MSAL to forget the current user so that a future request to acquire a token will succeed only if it is made to be interactive.
Although the application in this sample supports single users, MSAL supports scenarios where multiple accounts can be signed in at the same time. An example is an email application where a user has multiple accounts.
To display basic information about the token, add the following method to your MainWindow.xaml.cs file:
/// <summary>
/// Display basic information contained in the token
/// </summary>
private void DisplayBasicTokenInfo(AuthenticationResult authResult)
{
TokenInfoText.Text = "";
if (authResult != null)
{
TokenInfoText.Text += $"Name: {authResult.User.Name}" + Environment.NewLine;
TokenInfoText.Text += $"Username: {authResult.User.DisplayableId}" + Environment.NewLine;
TokenInfoText.Text += $"Token Expires: {authResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
TokenInfoText.Text += $"Access Token: {authResult.AccessToken}" + Environment.NewLine;
}
}
In addition to the access token that's used to call the Microsoft Graph API, after the user signs in, MSAL also obtains an ID token. This token contain a small subset of information that's pertinent to users. The DisplayBasicTokenInfo
method displays the basic information that's contained in the token. For example, it displays the user's display name and ID, as well as the token expiration date and the string representing the access token itself. You can select the Call Microsoft Graph API button multiple times and see that the same token was reused for subsequent requests. You can also see the expiration date being extended when MSAL decides it is time to renew the token.