C# 为什么我的应用程序最终总是调用Program.PublicClientApp.AcquireTokenAsync?

C# 为什么我的应用程序最终总是调用Program.PublicClientApp.AcquireTokenAsync?,c#,oauth,azure-active-directory,microsoft-graph-api,msal,C#,Oauth,Azure Active Directory,Microsoft Graph Api,Msal,这是我的代码,用于对Outlook使用Microsoft Graph进行身份验证: public async Task AquireToken() { try { if (_AuthResult == null) { _AuthResult = await Program.PublicClientApp.AcquireTokenSilentAsync( _scopes, Program.Pub

这是我的代码,用于对Outlook使用Microsoft Graph进行身份验证:

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);
    }
}
它基于Microsoft提供的。控制台输出中显示:

代币到期时间:2017年9月4日14:18:06+01:00

该代码从以下位置显示:

$"Token Expires: {_AuthResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
因此,这意味着令牌在一小时内有效。因此,如果我再次运行我的实用程序,我希望它使用相同的令牌,直到它需要一个新的令牌。但事实并非如此。它总是显示提示

我错过了哪一步

例外 根据评论中的要求,以下是例外情况的详细信息:

MsalUiRequiredException:在AcquiretokenSilent API中传递了Null用户。传入用户对象或调用acquireToken进行身份验证

这可能会有帮助

我需要回顾提供的答案:

您需要实现令牌缓存并使用AcquireTokenSilentAsync。有一个web应用程序示例


桌面上的MSAL.NET不提供持久性缓存,因为它没有现成的存储(而MSAL在UWP、Xamarin iOS和Android上提供持久性存储,其中应用程序隔离存储可用)。开箱即用,桌面上的MSAL.NET使用inmemory缓存,该缓存将在进程结束后立即消失。
请参阅示例,以了解如何提供一个简单的基于文件的缓存,该缓存将在执行期间持久化令牌。

桌面上的MSAL.NET不提供持久化缓存,因为没有明显的现成存储(虽然MSAL确实在UWP、Xamarin iOS和Android上提供了持久性存储,其中应用程序隔离存储是可用的)。桌面上的MSAL.NET开箱即用,使用的是内存缓存,该缓存将在进程结束后立即消失。
请参阅演示如何提供一个简单的基于文件的缓存的示例,该缓存将在执行期间保留令牌。

我使用了注册表。成功登录后保存令牌,然后在每次需要使用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;
    }
公共静态异步任务GetAuthenticatedClientAsync()
{
GraphServiceClient graphClient=新GraphServiceClient(
新的DelegateAuthenticationProvider(
异步(请求消息)=>
{
字符串appID=ConfigurationManager.AppSettings[“ida:appID”];
PublicClientApplication PublicClientApp=新的PublicClientApplication(appID);
string[]_scopes=新字符串[]{“Calendars.read”、“Calendars.readwrite”、“Mail.read”、“User.read”};
AuthenticationResult authResult=null;
字符串keyName=@“软件\xxx\Security”;
字符串valueName=“Status”;
字符串标记=”;
RegistryKey regKey=Registry.CurrentUser.OpenSubKey(keyName,false);
if(regKey!=null)
{
令牌=(字符串)regKey.GetValue(valueName);
}
if(regKey==null | | string.IsNullOrEmpty(令牌))
{
authResult=await PublicClientApp.AcquireTokenAsync(_scopes);//打开Microsoft登录屏幕
//如果密钥不存在,则编码
注册表键;
key=Registry.CurrentUser.CreateSubKey(@“Software\xxx\Security”);
key.OpenSubKey(@“Software\xxx\Security”,true);
key.SetValue(“状态”,authResult.AccessToken);
SetValue(“Expire”,authResult.ExpiresOn.ToString());
key.Close();
//将访问令牌附加到请求。
requestMessage.Headers.Authorization=新的AuthenticationHeaderValue(“承载者”,令牌);
}
其他的
{
//如果密钥存在,则编码
RegistryKey reg=Registry.CurrentUser.OpenSubKey(@“Software\xxx\Login”,true);
//将“abc”的值设置为“efd”
令牌=(字符串)regKey.GetValue(valueName);
//将访问令牌附加到请求。
requestMessage.Headers.Authorization=新的AuthenticationHeaderValue(“承载者”,令牌);
}
}));
尝试
{      
Microsoft.Graph.User me=等待graphClient.me.Request().GetAsync();
}
捕获(例外e)
{
如果(e.ToString()包含(“访问令牌验证失败”)| e.ToString()包含(“访问令牌已过期”))
{
字符串keyName=@“软件\xxx\Security”;
使用(RegistryKey=Registry.CurrentUser.OpenSubKey(keyName,true))
{
if(key!=null)
{
键。删除值(“状态”);
key.DeleteValue(“到期”);
}
其他的
{
MessageBox.Show(“错误!出现问题。请与管理员联系。”,“错误!”,MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}
等待GetAuthenticatedClientSync();
}
}
返回图形客户端;
}

我使用了注册表。成功登录后保存令牌,然后在每次需要使用GraphServiceClient时回拨令牌。如果令牌已过期或出现错误,您可以调用登录过程