为什么我的应用程序总是以调用 Program.PublicClientApp.AcquireTokenAsync 结束?
Why does my application always end up calling Program.PublicClientApp.AcquireTokenAsync?
这是我的 authenticate 代码,用于将 Microsoft Graph 与 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);
}
}
它基于微软提供的samples。在控制台输出中显示:
Token Expires: 04/09/2017 14:18:06 +01:00
该代码显示自:
$"Token Expires: {_AuthResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
因此,这意味着令牌的有效期为一小时。因此,如果我再次 运行 我的实用程序,我希望它使用相同的令牌,直到它需要一个新的令牌。但事实并非如此。它总是显示提示。
我错过了哪一步?
异常
根据评论中的要求,这是异常的详细信息:
MsalUiRequiredException: Null user was passed in AcquiretokenSilent API. Pass in a user object or call acquireToken authenticate.
这可能有帮助
我需要查看提供的答案:
you need to implement a token cache and use AcquireTokenSilentAsync. https://docs.microsoft.com/en-us/outlook/rest/dotnet-tutorial has a web app example.
桌面上的 MSAL .NET 不提供持久缓存,因为它没有明显的开箱即用存储(而 MSAL 在 UWP、Xamarin iOS 和 Android,其中应用程序隔离存储可用)。开箱即用,桌面上的 MSAL .NET 使用内存缓存,该缓存将在进程结束后立即消失。
请参阅 https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-desktop-msgraph-v2/ 示例,该示例演示了如何提供一个简单的基于文件的缓存,该缓存将在执行过程中保留令牌。
我使用了注册表。成功登录后保存令牌,然后在每次需要使用 GraphServiceClient 时调用令牌。如果令牌已过期或出现错误,您可以调用登录过程并保存新令牌。
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;
}
这是我的 authenticate 代码,用于将 Microsoft Graph 与 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);
}
}
它基于微软提供的samples。在控制台输出中显示:
Token Expires: 04/09/2017 14:18:06 +01:00
该代码显示自:
$"Token Expires: {_AuthResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
因此,这意味着令牌的有效期为一小时。因此,如果我再次 运行 我的实用程序,我希望它使用相同的令牌,直到它需要一个新的令牌。但事实并非如此。它总是显示提示。
我错过了哪一步?
异常
根据评论中的要求,这是异常的详细信息:
MsalUiRequiredException: Null user was passed in AcquiretokenSilent API. Pass in a user object or call acquireToken authenticate.
这可能有帮助
我需要查看提供的答案:
you need to implement a token cache and use AcquireTokenSilentAsync. https://docs.microsoft.com/en-us/outlook/rest/dotnet-tutorial has a web app example.
桌面上的 MSAL .NET 不提供持久缓存,因为它没有明显的开箱即用存储(而 MSAL 在 UWP、Xamarin iOS 和 Android,其中应用程序隔离存储可用)。开箱即用,桌面上的 MSAL .NET 使用内存缓存,该缓存将在进程结束后立即消失。 请参阅 https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-desktop-msgraph-v2/ 示例,该示例演示了如何提供一个简单的基于文件的缓存,该缓存将在执行过程中保留令牌。
我使用了注册表。成功登录后保存令牌,然后在每次需要使用 GraphServiceClient 时调用令牌。如果令牌已过期或出现错误,您可以调用登录过程并保存新令牌。
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;
}