【问题标题】:Microsoft.IdentityModel.Clients.ActiveDirectory.AdalSilentTokenAcquisitionException: Failed to acquire token silently. Call method AcquireTokenMicrosoft.IdentityModel.Clients.ActiveDirectory.AdalSilentTokenAcquisitionException:无法静默获取令牌。调用方法 AcquireToken
【发布时间】:2017-02-17 10:08:29
【问题描述】:

我使用内置的“配置 Azure AD 身份验证”功能配置了我的 MVC webapp,它为我提供了以下代码:

public 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 database
        AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID, new ADALTokenCache(signedInUserID));
        AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(graphResourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
        return authenticationResult.AccessToken;
    }

这是模型

public class ADALTokenCache : TokenCache
{
    private ApplicationDbContext db = new ApplicationDbContext();
    private string userId;
    private UserTokenCache Cache;

    public ADALTokenCache(string signedInUserId)
    {
        // associate the cache to the current user of the web app
        userId = signedInUserId;
        this.AfterAccess = AfterAccessNotification;
        this.BeforeAccess = BeforeAccessNotification;
        this.BeforeWrite = BeforeWriteNotification;
        // look up the entry in the database
        Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
        // place the entry in memory
        this.Deserialize((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits,"ADALCache"));
    }

    // clean up the database
    public override void Clear()
    {
        base.Clear();
        var cacheEntry = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
        db.UserTokenCacheList.Remove(cacheEntry);
        db.SaveChanges();
    }

    // Notification raised before ADAL accesses the cache.
    // This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
    void BeforeAccessNotification(TokenCacheNotificationArgs args)
    {
        if (Cache == null)
        {
            // first time access
            Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
        }
        else
        { 
            // retrieve last write from the DB
            var status = from e in db.UserTokenCacheList
                         where (e.webUserUniqueId == userId)
            select new
            {
                LastWrite = e.LastWrite
            };

            // if the in-memory copy is older than the persistent copy
            if (status.First().LastWrite > Cache.LastWrite)
            {
                // read from from storage, update in-memory copy
                Cache = db.UserTokenCacheList.FirstOrDefault(c => c.webUserUniqueId == userId);
            }
        }
        this.Deserialize((Cache == null) ? null : MachineKey.Unprotect(Cache.cacheBits, "ADALCache"));
    }

    // Notification raised after ADAL accessed the cache.
    // If the HasStateChanged flag is set, ADAL changed the content of the cache
    void AfterAccessNotification(TokenCacheNotificationArgs args)
    {
        // if state changed
        if (this.HasStateChanged)
        {
            Cache = new UserTokenCache
            {
                webUserUniqueId = userId,
                cacheBits = MachineKey.Protect(this.Serialize(), "ADALCache"),
                LastWrite = DateTime.Now
            };
            // update the DB and the lastwrite 
            db.Entry(Cache).State = Cache.UserTokenCacheId == 0 ? EntityState.Added : EntityState.Modified;
            db.SaveChanges();
            this.HasStateChanged = false;
        }
    }

    void BeforeWriteNotification(TokenCacheNotificationArgs args)
    {
        // if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
    }

    public override void DeleteItem(TokenCacheItem item)
    {
        base.DeleteItem(item);
    }
}

数据库上下文:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext()
        : base("AzureDatabase")
    {
    }

    public DbSet<UserTokenCache> UserTokenCacheList { get; set; }
}

public class UserTokenCache
{
    [Key]
    public int UserTokenCacheId { get; set; }
    public string webUserUniqueId { get; set; }
    public byte[] cacheBits { get; set; }
    public DateTime LastWrite { get; set; }
}

起初,所有令牌缓存都存储在本地数据库中,该数据库由连接到名为“defaultconnection”的数据库的函数自动配置。然而,当我试图发布我的网络应用程序时,我遇到了错误,我意识到这是因为所有令牌都存储在本地。所以我尝试将 DBContext 迁移到我的 Azure 数据库。迁移后,我不断收到此异常。有没有办法解决这个问题??

【问题讨论】:

  • 您在 Azure 端的数据库看起来是否相同?

标签: c# azure asp.net-mvc-5 azure-active-directory adal


【解决方案1】:

根据我的理解,在调用AcquireTokenSilentAsync方法之前,我们应该在不使用刷新令牌的情况下执行获取令牌。

例如,我们可以在用户登录时web应用获取授权码后获取token。下面是一段代码供大家参考:

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        ClientId = SettingsHelper.ClientId,
        Authority = SettingsHelper.Authority,                

        Notifications = new OpenIdConnectAuthenticationNotifications()
        {
            //
            // If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.

            AuthorizationCodeReceived = (context) =>
            {
                var code = context.Code;

                ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
                String UserObjectId = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;

                AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.Authority, new ADALTokenCache(UserObjectId));

                authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, SettingsHelper.AADGraphResourceId);
                //authContext.TokenCache.Clear();
                return Task.FromResult(0);
            }
        }
    });

您可以参考完整的代码示例here 以使用AcquireTokenSilentAsync 函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 2021-09-06
    • 1970-01-01
    相关资源
    最近更新 更多