This is my code to authenticate for using Microsoft Graph with Outlook:
public async Task AquireToken()
{
try
{
if (_AuthResult == null)
{
_AuthResult = await Program.PublicClientApp.AcquireTokenSilentAsync(
_scopes, Program.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 Program.PublicClientApp.AcquireTokenAsync(_scopes);
}
catch (MsalException msalex)
{
_ResultsText = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
}
}
catch (Exception ex)
{
_ResultsText = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
}
if (_AuthResult != null)
{
_ResultsText = await GetHttpContentWithToken(_graphAPIEndpoint, _AuthResult.AccessToken);
}
}
It is based on the samples provided by Microsoft. In the console output it says:
Token Expires: 04/09/2017 14:18:06 +01:00
That code is displayed from:
$"Token Expires: {_AuthResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
Thus, this implies that the token is valid for one hour. So if I run my utility again I am expecting it to use the same token until it needs to ask for a new one. But it doesn't. It always shows the prompt.
What step have I missed?
As per the request in the comments, this is the details from the exception:
MsalUiRequiredException: Null user was passed in AcquiretokenSilent API. Pass in a user object or call acquireToken authenticate.
I need to review the answer provided:
you need to implement a token cache and use AcquireTokenSilentAsync. https://learn.microsoft.com/en-us/outlook/rest/dotnet-tutorial has a web app example.
I made use of the registry. Save the token when you get a successful log in then call the token back each time you need to make use of the GraphServiceClient
. If the token has expired or an error appears you can recall the log in process and save the new token.
public static async Task<GraphServiceClient> GetAuthenticatedClientAsync()
{
GraphServiceClient graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string appID = ConfigurationManager.AppSettings["ida:AppId"];
PublicClientApplication PublicClientApp = new PublicClientApplication(appID);
string[] _scopes = new string[] { "Calendars.read", "Calendars.readwrite", "Mail.read", "User.read" };
AuthenticationResult authResult = null;
string keyName = @"Software\xxx\Security";
string valueName = "Status";
string token = "";
RegistryKey regKey = Registry.CurrentUser.OpenSubKey(keyName, false);
if (regKey != null)
{
token = (string)regKey.GetValue(valueName);
}
if (regKey == null || string.IsNullOrEmpty(token))
{
authResult = await PublicClientApp.AcquireTokenAsync(_scopes); //Opens Microsoft Login Screen
//code if key Not Exist
RegistryKey key;
key = Registry.CurrentUser.CreateSubKey(@"Software\xxx\Security");
key.OpenSubKey(@"Software\xxx\Security", true);
key.SetValue("Status", authResult.AccessToken);
key.SetValue("Expire", authResult.ExpiresOn.ToString());
key.Close();
// Append the access token to the request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
else
{
//code if key Exists
RegistryKey reg = Registry.CurrentUser.OpenSubKey(@"Software\xxx\Login", true);
// set value of "abc" to "efd"
token = (string)regKey.GetValue(valueName);
// Append the access token to the request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
}));
try
{
Microsoft.Graph.User me = await graphClient.Me.Request().GetAsync();
}
catch(Exception e)
{
if (e.ToString().Contains("Access token validation failure") || e.ToString().Contains("Access token has expired"))
{
string keyName = @"Software\xxx\Security";
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName, true))
{
if (key != null)
{
key.DeleteValue("Status");
key.DeleteValue("Expire");
}
else
{
MessageBox.Show("Error! Something went wrong. Please contact your administrator.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
await GetAuthenticatedClientAsync();
}
}
return graphClient;
}