【问题标题】:AcquireTokenSilent is Failing to acquire token silently?AcquireTokenSilent 无法静默获取令牌?
【发布时间】:2017-02-25 21:09:50
【问题描述】:

我在 Azure 上有一个托管应用程序。在 SiteMaster 页面(母版页)中,我试图获取所有用户的 AD 组,因为我的应用程序是基于角色的应用程序,其中每个用户都属于一个 Azure 组,并且每个组都可以执行某些功能。

我在页面加载事件中的代码如下:

private static string clientId = ConfigurationManager.AppSettings["ida:ClientID"];
private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string graphResourceId = "https://graph.windows.net";

protected void Page_Load(object sender, EventArgs e)
{
      if (Request.IsAuthenticated)
      {
          IList<string> groups = GetUserData();
      }
}

请注意,我正在调用函数“GetUserData()”,它实际上带来了用户所属的所有组。该函数的代码如下:

        public IList<string> GetUserData()
        {
            string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
            string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

            Uri servicePointUri = new Uri(graphResourceId);
            Uri serviceRoot = new Uri(servicePointUri, tenantID);
            ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot,
                    async () => await GetTokenForApplication());

            IList<string> groupMembership = new List<string>();
            // use the token for querying the graph to get the user details
            IUser user = activeDirectoryClient.Users
                .Where(u => u.ObjectId.Equals(userObjectID))
                .ExecuteAsync().Result.CurrentPage.ToList().First();
            var userFetcher = (IUserFetcher)user;
            requestor = user.DisplayName;
            IPagedCollection<IDirectoryObject> pagedCollection = userFetcher.MemberOf.ExecuteAsync().Result;
            do
            {
                List<IDirectoryObject> directoryObjects = pagedCollection.CurrentPage.ToList();
                foreach (IDirectoryObject directoryObject in directoryObjects)
                {
                    if (directoryObject is Group)
                    {
                        var group = directoryObject as Group;
                        groupMembership.Add(group.DisplayName);
                    }
                }
                pagedCollection = pagedCollection.GetNextPageAsync().Result;
            } while (pagedCollection != null);

            return groupMembership;
        }

“GetUserData()”函数正在调用另一个名为“GetTokenForApplication()”的函数,该函数负责从 Azure 获取令牌。上一个函数的源码如下:

  protected async Task<string> GetTokenForApplication()
    {
        string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
        string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
        string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

        // get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
        ClientCredential clientcred = new ClientCredential(clientId, appKey);
        // initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's EF DB
        AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID, new ADALTokenCache(signedInUserID));
        AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(graphResourceId, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
        return authenticationResult.AccessToken;


    } 

我的问题有时是当用户尝试登录我的应用程序时,她/他收到错误Failed to acquire token silently. Call method AcquireToken

此错误发生在我不知道的某些情况下,当它发生时,它不会影响所有用户。一些用户仍然能够进入并且没有收到此错误。关于为什么会发生这种情况以及如何解决此类错误的任何建议或想法?

这是我的堆栈跟踪:

[AdalSilentTokenAcquisitionException: Failed to acquire token silently. Call method AcquireToken]
   Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenSilentHandler.SendTokenRequestAsync() +98
   Microsoft.IdentityModel.Clients.ActiveDirectory.<RunAsync>d__0.MoveNext() +1807
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.IdentityModel.Clients.ActiveDirectory.<AcquireTokenSilentCommonAsync>d__10.MoveNext() +317
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.IdentityModel.Clients.ActiveDirectory.<AcquireTokenSilentAsync>d__5c.MoveNext() +268
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   MRTWebApplication.<GetTokenForApplication>d__6.MoveNext() +539
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   MRTWebApplication.<<GetUserData>b__5_0>d.MoveNext() +194
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.Azure.ActiveDirectory.GraphClient.Extensions.<SetToken>d__1.MoveNext() +207
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.Azure.ActiveDirectory.GraphClient.Extensions.<ExecuteAsync>d__4d`2.MoveNext() +986
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.Azure.ActiveDirectory.GraphClient.Extensions.<<ExecuteAsync>b__0>d__2.MoveNext() +263

[AggregateException: One or more errors occurred.]
   System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) +4465776
   MRTWebApplication._Default.GetUserData() +778
   MRTWebApplication._Default.Page_Load(Object sender, EventArgs e) +43
   System.Web.UI.Control.OnLoad(EventArgs e) +109
   System.Web.UI.Control.LoadRecursive() +68
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +4498

谢谢!

【问题讨论】:

  • 请编辑您的问题并将所有堆栈跟踪仅放在那里。谢谢。
  • @GauravMantri 我将堆栈跟踪添加到问题中

标签: c# asp.net azure token adal


【解决方案1】:

AcquireTokenSilentAsync 将尽一切可能为您获取有效令牌,而不会提示您进行交互式身份验证请求。如果它不能(通过缓存或刷新令牌),那么它将产生一个异常。

您可以捕获this exception 并决定提示进行交互式身份验证或静默失败并要求最终用户稍后根据您的应用逻辑登录。 Here 是此交互式调用的参考文档。

            AuthenticationResult result;
            try
            {
                 result = await authContext.AcquireTokenSilentAsync(resourceId, clientId);
            }
            catch (AdalException ex)
            {
                if (ex.ErrorCode == "failed_to_acquire_token_silently")
                {
                    // There are no tokens in the cache. 
                    result = await authContext.AcquireTokenAsync(resourceId, clientId, redirectUri, new PlatformParameters(PromptBehavior.Always));

                }
                else
                {
                    // An unexpected error occurred.
                    string message = ex.Message;
                    if (ex.InnerException != null)
                    {
                        message += "Inner Exception : " + ex.InnerException.Message;
                    }
                    MessageBox.Show(message);
                }

Here 是一个很棒的代码示例 .NET MVC 应用程序,它向您展示了如何使用 ADAL。

【讨论】:

  • 感谢@DanielDobalian 的回答。我也想过同样的事情。如果没有令牌,如何让应用程序强制用户重新登录?
  • 查看result = await authContext.AcquireTokenAsync(resourceId, clientId, redirectUri, new PlatformParameters(PromptBehavior.Always)); 行。这将强制进行交互身份验证请求。
  • 代码示例似乎已经过时了,都是关于 Owin 而不是对 AcquireToken 的单一引用。
  • @Wolfzoon 好点,该示例都是 OWIN OIDC,Here's 显示 OWIN + ADAL 行为的代码示例。
  • 我尝试了这种方法并以隐身方式打开,但它不适合我
【解决方案2】:

AFAIK,当您尝试静默获取令牌时(调用 AcquireTokenSilentAsync 以获取 accessToken ),它将从 TokenCache 获取令牌或静默使用 refreshToken 。如果访问令牌和刷新令牌过期,您可能会得到 AdalSilentTokenAcquisitionException 。您必须捕获 AdalSilentTokenAcquisitionException 异常:

try
{
    AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID, new ADALTokenCache(signedInUserID));
    AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(graphResourceId, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
    return authenticationResult.AccessToken;
}
catch (AdalSilentTokenAcquisitionException)
{
    //told or force the user to reauthentificate.

    return null;
}

在异常句柄中,您可以告诉用户重新认证或直接执行OAuth认证过程。

【讨论】:

  • 是否有类似的东西,而不是通知用户重新认证,它应该使用可用参数生成新令牌?
  • reauth 需要用户名/密码对吗?浏览器也不能帮你点击登录按钮甚至记住用户名和密码,这是出于安全考虑
猜你喜欢
  • 2015-06-26
  • 1970-01-01
  • 2015-07-10
  • 1970-01-01
  • 2016-07-12
  • 2016-08-12
  • 2017-10-08
  • 1970-01-01
  • 2023-03-10
相关资源
最近更新 更多