C# &引用;无法以静默方式获取令牌";尝试连接到Microsoft Graph时出错

C# &引用;无法以静默方式获取令牌";尝试连接到Microsoft Graph时出错,c#,asp.net-mvc,azure-active-directory,owin,microsoft-graph-api,C#,Asp.net Mvc,Azure Active Directory,Owin,Microsoft Graph Api,我们有一个asp.net MVC应用程序,正在使用OpenID connect对Azure AD进行身份验证。用户在启动时使用以下代码进行身份验证: public void ConfigureAuth(IAppBuilder app) { ApplicationDbContext db = new ApplicationDbContext(); app.SetDefaultSignInAsAuthenticationType(CookieAuthenti

我们有一个asp.net MVC应用程序,正在使用OpenID connect对Azure AD进行身份验证。用户在启动时使用以下代码进行身份验证:

public void ConfigureAuth(IAppBuilder app)
    {
        ApplicationDbContext db = new ApplicationDbContext();

        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = ConfigHelper.ClientId,
                Authority = ConfigHelper.Authority,
                PostLogoutRedirectUri = ConfigHelper.PostLogoutRedirectUri,

                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(ConfigHelper.ClientId, ConfigHelper.ClientSecret);
                        string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
                        AuthenticationContext authContext = new AuthenticationContext(ConfigHelper.Authority, new ADALTokenCache(signedInUserID));
                        return authContext.AcquireTokenByAuthorizationCodeAsync(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, ConfigHelper.GraphResourceId);
                    }
                }
            });
    }
到目前为止,用户已成功通过身份验证,但在应用程序的其他地方,我们正尝试使用帮助器类中的以下代码片段对Microsoft Graph进行身份验证/连接:

ClientCredential cred = new ClientCredential(ConfigHelper.ClientId, ConfigHelper.ClientSecret);
        AuthenticationContext authContext = new AuthenticationContext(ConfigHelper.Authority, new ADALTokenCache(signedInUserID));
        try
        {
            AuthenticationResult result = await authContext.AcquireTokenSilentAsync(ConfigHelper.GraphResourceId, cred, new UserIdentifier(signedInUserID, UserIdentifierType.UniqueId));
            return result.AccessToken;
        }
        catch (AdalSilentTokenAcquisitionException e)
        {
            // handle exception
        }
此处,
AcquireTokenSilentAsync
方法总是失败,原因如下:

Failed to acquire token silently as no token was found in the cache. Call method AcquireToken

Microsoft.IdentityModel.Clients.ActiveDirectory

at Microsoft.IdentityModel.Clients.ActiveDirectory.Internal.Flows.AcquireTokenSilentHandler.SendTokenRequestAsync()
at Microsoft.IdentityModel.Clients.ActiveDirectory.Internal.Flows.AcquireTokenHandlerBase.<CheckAndAcquireTokenUsingBrokerAsync>d__59.MoveNext()

从来没有真正弄清楚这一点,但通过在VisualStudio中删除连接的服务并再次使用AAD和Graph连接的服务添加身份验证,成功地解决了这一问题。这当然是用“干净”版本重写了一些项目文件,但似乎已经做到了


感谢juunas 4观看。

嗯,乍一看很不错。您在获取令牌时是否在AccessNotification之前调试了
?是的,我调试过。命令
db.UserTokenCacheList.FirstOrDefault
在构造函数和
BeforeAccessNotification
中都返回null,而在应用程序启动时它确实返回
UserTokenCache
。您确定数据库中存在具有该id的条目吗?你有没有检查过这些字符是同一个大小写?(以防您的排序规则区分大小写)是的,检查过了,并且再次检查过。事实上,那不是真的。我一直在调试中检查并反复检查传递的id是否相同,但id并没有按原样写入数据库,而是看起来像某种散列。仍然令人困惑的是,为什么它能够在某一点读取哈希值,但在另一点却无法读取。
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);
    }
}