问题描述
我有一个在 azure 上运行的 .NET MVC Core 3.1 Webapp。此 Web 应用程序使用针对 Azure AD 的 SSO,并在委托模式下使用 powerbi API 和图形 API。
一切正常,但现在我经常在触发 AcquiretokenSilentAsync 时出现 Failed_to_acquire_token_silently 异常。这不是 100% 的情况,而且对我来说有点随机。
Startup.cs/ConfigureServices:
services.AddAuthentication("Azures").AddPolicyScheme("Azures","Authorize AzureAd or AzureAdBearer",options =>
{
options.ForwardDefaultSelector = context =>
{
....
};
})
.AddJwtBearer(x =>
{
.....
})
// For browser access
.AddAzureAD(options => Configuration.Bind("AzureAd",options));
Startup.cs/ConfiguretokenHandling:
private void ConfiguretokenHandling(IServiceCollection services)
{
if (Configuration["AuthWithAppSecret:ClientSecret"] != "")
{
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme,options =>
{
options.ResponseType = Configuration["AuthWithAppSecret:ResponseType"];
options.ClientSecret = Configuration["AuthWithAppSecret:ClientSecret"];
options.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async ctx =>
{
HttpRequest request = ctx.HttpContext.Request;
//We need to also specify the redirect URL used
string currentUri = UriHelper.BuildAbsolute(request.Scheme,request.Host,request.PathBase,request.Path);
//Credentials for app itself
var credential = new ClientCredential(ctx.Options.ClientId,ctx.Options.ClientSecret);
//Construct token cache
ITokenCacheFactory cacheFactory = ctx.HttpContext.RequestServices.GetrequiredService<ITokenCacheFactory>();
TokenCache cache = cacheFactory.CreateForUser(ctx.Principal);
var authContext = new AuthenticationContext(ctx.Options.Authority,cache);
string resource = Configuration["PowerBI:PowerBiResourceUrl"];
AuthenticationResult result = await authContext.AcquiretokenByAuthorizationCodeAsync(
ctx.ProtocolMessage.Code,new Uri(currentUri),credential,resource);
//Tell the OIDC middleware we got the tokens,it doesn't need to do anything
ctx.HandleCodeRedemption(result.Accesstoken,result.IdToken);
}
};
});
}
}
控制器是这样的:
public class ProjectsController : BaseController
{
private readonly ITokenCacheFactory _tokenCacheFactory;
public ProjectsController(MyContext context,IConfiguration configuration,ITokenCacheFactory tokenCacheFactory)
{
_context = context;
_tokenCacheFactory = tokenCacheFactory;
_configuration = configuration;
}
后来由控制器触发:
static public async Task<string> GetAccesstokenAsync2(IConfiguration _configuration,ITokenCacheFactory _tokenCacheFactory,ClaimsPrincipal User,string resURL,Uri redirectURI)
{
string authority = _configuration["AzureAd:Authority"];
string clientId = _configuration["AzureAd:ClientId"];
string clientSecret = _configuration["AuthWithAppSecret:ClientSecret"];
var cache = _tokenCacheFactory.CreateForUser(User);
var authContext = new AuthenticationContext(authority,cache);
var credential = new ClientCredential(clientId,clientSecret);
var userId = User.GetobjectId();
AuthenticationResult result;
try
{
result = await authContext.AcquiretokenSilentAsync(
resURL,new UserIdentifier(userId,UserIdentifierType.UniqueId));
}
catch (ADalexception ex)
{
mylog.Info("GetAccesstokenAsync - Adal Ex:" + ex.ErrorCode);
if (ex.ErrorCode == "Failed_to_acquire_token_silently")
{
// There are no tokens in the cache.
try
{
PlatformParameters param = new PlatformParameters();
result = await authContext.AcquiretokenAsync(resURL,clientId,redirectURI,param,UserIdentifierType.UniqueId));
}
catch (Exception e)
{
mylog.Error("GetAccesstokenAsync - AcquiretokenAsync" + e.ToString());
throw e;
}
}
else
throw ex;
}
return result.Accesstoken;
}
已添加 AcquiretokenAsync 以解决 Failed_to_acquire_token_silently 问题(但完全失败)。
你知道为什么它不时失败吗? 任何其他想法如何解决它?
谢谢!!! 克里斯蒂安
编辑 07/04: 举个例子:
2021-04-07 15:18:24.674 +00:00 OnAuthorizationCodeReceived is triggered for user fd918ddf-fbb9-40d2-812b-b01876118f42
2021-04-07 15:18:31.675 +00:00 AcquiretokenSilentAsync - trigger exception userId 'fd918ddf-fbb9-40d2-812b-b01876118f42'
用户已正确通过 AD 身份验证。收到一个代码,几秒钟后出现 Failed_to_acquire_token_silently 异常。
解决方法
错误 failed_to_acquire_token_silently 在缓存中找不到访问令牌或访问令牌已过期时发生。
代码示例 here:
// STS
string cloud = "https://login.microsoftonline.com";
string tenantId = "331e6716-26e8-4651-b323-2563936b416e";
string authority = $"{cloud}/{tenantId}";
// Application
string clientId = "65b27a1c-693c-44bf-bf92-c49e408ccc70";
Uri redirectUri = new Uri("https://TodoListClient");
// Application ID of the Resource (could also be the Resource URI)
string resource = "eab51d24-076e-44ee-bcf0-c2dce7577a6a";
AuthenticationContext ac = new AuthenticationContext(authority);
AuthenticationResult result=null;
try
{
result = await ac.AcquireTokenSilentAsync(resource,clientId);
}
catch (AdalException adalException)
{
if (adalException.ErrorCode == AdalError.FailedToAcquireTokenSilently
|| adalException.ErrorCode == AdalError.InteractionRequired)
{
result = await ac.AcquireTokenAsync(resource,clientId,redirectUri,new PlatformParameters(PromptBehavior.Auto));
}
}
请注意,客户端凭据流中不需要调用 AcquireTokenSilent(当应用程序在没有 用户,但以自己的名义)
但是您在代码中使用了 client credentials flow,您可以通过 AcquireTokenAsync
获取访问令牌。
clientCredential = new ClientCredential(clientId,appKey);
AuthenticationContext authenticationContext =
new AuthenticationContext("https://login.microsoftonline.com/<tenantId>");
AuthenticationResult result =
await authenticationContext.AcquireTokenAsync("https://resourceUrl",clientCredential);