Ошибка «Не удалось получить токен молча» при попытке подключения к Microsoft Graph - PullRequest
0 голосов
/ 14 сентября 2018

У нас есть приложение asp.net MVC, которое проходит аутентификацию в Azure AD с OpenID connect. При запуске пользователи проходят проверку с использованием следующего кода:

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()

В обоих случаях TokenCache создается с вызовом new ADALTokenCache(signedInUserID). Класс ADALTokenCache использует Entity Framework для сохранения токенов в базе данных SQL-сервера Azure. Проходя по коду, мы видим, что во время запуска приложения ADALTokenCache успешно сохраняется и считывается из базы данных при вызове AcquireTokenByAuthorizationCodeAsync, но при вызове AcquireTokenSilentAsync кэш токена возвращает ноль, несмотря на то, что все переменные одинаковы.

Нам не удалось выяснить, почему это удастся в первом случае, но не удастся во втором. Мы также не смогли выяснить, связано ли это с OWIN, с Graph или Entity Framework.

Или для этого сценария существует более предпочтительный метод аутентификации пользователей, кроме OWIN?

Любая помощь высоко ценится.

Вот наш ADALTokenCache класс:

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);
    }
}

1 Ответ

0 голосов
/ 14 сентября 2018

На самом деле никогда не доходил до сути, но сумел разобраться, удалив подключенные сервисы в visual studio и снова добавив аутентификацию с подключенными сервисами AAD и Graph.Это, конечно, переписало несколько файлов проекта с «чистыми» версиями, но, похоже, сработало.

Спасибо juunas 4 за то, что взглянули.

...