AcquireTokenSilent 无法静默获取令牌?
AcquireTokenSilent is Failing to acquire token silently?
我在 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;
}
我的问题有时是当用户尝试登录我的应用程序时 s/he 收到错误 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
谢谢!
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 验证过程。
AcquireTokenSilentAsync 将尝试所有可能的方法来为您获取有效令牌,而无需提示交互式身份验证请求。如果它不能(通过缓存或刷新令牌),那么它将生成一个异常。
你可以抓住this exception and decide to prompt for an interactive auth or silently fail and ask your end users to sign in later depending on your app logic. 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。
我在 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;
}
我的问题有时是当用户尝试登录我的应用程序时 s/he 收到错误 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
谢谢!
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 验证过程。
AcquireTokenSilentAsync 将尝试所有可能的方法来为您获取有效令牌,而无需提示交互式身份验证请求。如果它不能(通过缓存或刷新令牌),那么它将生成一个异常。
你可以抓住this exception and decide to prompt for an interactive auth or silently fail and ask your end users to sign in later depending on your app logic. 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。