Код: TokenNotFound Сообщение: пользователь не найден в кэше токена. Может быть, сервер был перезапущен - PullRequest
0 голосов
/ 04 мая 2018

У меня есть следующая функция для вызова пользователей из активного каталога, используя graph api. Эта функция нажата при каждом нажатии на текстовое поле. Но я получаю следующую ошибку

Код: TokenNotFound Сообщение: пользователь не найден в кэше токена. Может быть сервер был перезапущен.

на линии

var user = await graphClient.Users.Request().GetAsync();

Вся функция ниже:

     public async Task<string> GetUsersJSONAsync(string textValue)
            {
               // email = email ?? User.Identity.Name ?? User.FindFirst("preferred_username").Value;
                var identifier = User.FindFirst(Startup.ObjectIdentifierType)?.Value;
                var graphClient = _graphSdkHelper.GetAuthenticatedClient(identifier);
                string usersJSON = await GraphService.GetAllUserJson(graphClient, HttpContext, textValue);
                return usersJSON;
            }


    public static async Task<string> GetAllUserJson(GraphServiceClient graphClient, HttpContext httpContext, string textValue)
            {
               // if (email == null) return JsonConvert.SerializeObject(new { Message = "Email address cannot be null." }, Formatting.Indented);

                try
                {
                    // Load user profile.
                    var user = await graphClient.Users.Request().GetAsync();                    
                    return JsonConvert.SerializeObject(user.Where(u => !string.IsNullOrEmpty(u.Surname) && ( u.Surname.ToLower().StartsWith(textValue) || u.Surname.ToUpper().StartsWith(textValue.ToUpper()))), Formatting.Indented);
                }
                catch (ServiceException e)
                {
                    switch (e.Error.Code)
                    {
                        case "Request_ResourceNotFound":
                        case "ResourceNotFound":
                        case "ErrorItemNotFound":
                        //case "itemNotFound":
                        //    return JsonConvert.SerializeObject(new { Message = $"User '{email}' was not found." }, Formatting.Indented);
                        //case "ErrorInvalidUser":
                        //    return JsonConvert.SerializeObject(new { Message = $"The requested user '{email}' is invalid." }, Formatting.Indented);
                        case "AuthenticationFailure":
                            return JsonConvert.SerializeObject(new { e.Error.Message }, Formatting.Indented);
                        case "TokenNotFound":
                            await httpContext.ChallengeAsync();
                            return JsonConvert.SerializeObject(new { e.Error.Message }, Formatting.Indented);
                        default:
                            return JsonConvert.SerializeObject(new { Message = "An unknown error has occured." }, Formatting.Indented);
                    }
                }
            }


 // Gets an access token. First tries to get the access token from the token cache.
        // Using password (secret) to authenticate. Production apps should use a certificate.
        public async Task<string> GetUserAccessTokenAsync(string userId)
        {
            _userTokenCache = new SessionTokenCache(userId, _memoryCache).GetCacheInstance();

            var cca = new ConfidentialClientApplication(
                _appId,
                _redirectUri,
                _credential,
                _userTokenCache,
                null);

            if (!cca.Users.Any()) throw new ServiceException(new Error
            {
                Code = "TokenNotFound",
                Message = "User not found in token cache. Maybe the server was restarted."
            });

            try
            {
                var result = await cca.AcquireTokenSilentAsync(_scopes, cca.Users.First());
                return result.AccessToken;
            }

            // Unable to retrieve the access token silently.
            catch (Exception)
            {
                throw new ServiceException(new Error
                {
                    Code = GraphErrorCode.AuthenticationFailure.ToString(),
                    Message = "Caller needs to authenticate. Unable to retrieve the access token silently."
                });
            }
        }

Можете ли вы помочь, что происходит не так?

Ответы [ 2 ]

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

Я знаю, что это 4 месяца - это все еще проблема для вас?

Как указывал предыдущий респондент, ошибка, которую вы видите, выдается в блоке catch в вашем коде, предназначенном для обработки пустой коллекции пользователей.

В случае, если вы застряли на этом, или кто-то еще пришел сюда - если вы использовали этот образец (или использовали ConfidentialClientApplication в любом отношении) и выбрасываете это исключение, это потому, что ваш _userTokenCache имеет нет пользователей *. Конечно, это не потому, что у вашей AD нет пользователей, иначе вы не сможете пройти аутентификацию. Скорее всего, это связано с тем, что устаревший файл cookie в вашем браузере передается в качестве маркера доступа вашему authProvider. Вы можете использовать Fiddler (или просто проверить файлы cookie вашего локального браузера), чтобы найти его (должен называться AspNetCore.Cookies, но вы можете захотеть удалить их все).

Если вы храните токен-кеш в сеансе (как в примере), помните, что при каждом запуске и остановке приложения ваша рабочая память будет выбрасываться, поэтому токен, предоставленный вашим браузером, больше не будет соответствовать новому одна ваша программа получит при повторном запуске (если, опять же, вы не очистили куки браузера).

*cca.Users больше не используется или не поддерживается MSAL - вы должны использовать cca.GetAccountsAsync(). Если у вас есть развернутое приложение, запущенное с устаревшей реализацией IUser, вам придется это изменить. В противном случае в процессе разработки ваш компилятор будет жаловаться и не позволит вам собрать, поэтому вы уже будете знать об этом.

0 голосов
/ 18 июля 2018

Глядя на код, кажется, что некоторые кусочки логики отсутствуют. Например, вы получили метод

public async Task<string> GetUserAccessTokenAsync(string userId)

но я не вижу, как это называется. Кроме того, я не вижу кода для извлечения токена из Azure AD. Наконец, сообщение об ошибке, которое вы упоминаете

Код: TokenNotFound Сообщение: пользователь не найден в кэше токена. Возможно, сервер был перезапущен.

Похоже на ошибку, которую вы выдаете

if (!cca.Users.Any()) throw new ServiceException(new Error
{
     Code = "TokenNotFound",
     Message = "User not found in token cache. Maybe the server was restarted."
});

Поскольку код не завершен, я постараюсь сделать предположение о том, что может пойти не так.

Во-первых, если вы используете MSAL.Net, то этап получения токена отсутствует.

Общий поток (Использование GetTokenByAuthorizationCodeAsync())

  1. Клиент бросает вызов пользователю
  2. Пользователь перенаправляется и входит в систему
  3. Вызывается обратный вызов, и клиент получает code от процесса входа в систему
  4. Передайте код в GetTokenByAuthorizationCodeAsync(), чтобы получить id_token и, в зависимости от разрешений, токен доступа. GetTokenByAuthorizationCodeAsync() будет хранить токен в кеше который был предоставлен ConfidentialClientApplication
  5. Получить токен из кэша с помощью AcquireTokenSilentAsync()
  6. Если нам не удастся получить токен из кэша с помощью AcquireTokenSilentAsync(), мы запросим новый через AcquireTokenAsync()

Большая часть этого потока, кажется, имеет место в вашем коде, но, возможно, вы упускаете фактическое получение токена. Поскольку токен не извлекается, пользователь не добавляется в ConfidentialClientApplication, что означает, что cca.Users.Any() возвращает false, что приводит к ServiceError

Предполагая, что весь поток на месте, и вы на самом деле получаете токен, мое второе предположение было бы, что _memoryCache отличаются. _MemoryCache, в котором вы сохранили свой токен, отличается от того, который вы используете для автоматического получения токена.

Я бы порекомендовал прочитать документацию о получении токена , чтобы определить, какой тип поиска подходит для вашего приложения.

РЕДАКТИРОВАТЬ

На самом деле, я предполагаю, что ваш код вдохновлен этим примером . Что особенно интересно, эта часть

public GraphServiceClient GetAuthenticatedClient(string userId)
    {
        _graphClient = new GraphServiceClient(new DelegateAuthenticationProvider(
        async requestMessage =>
        {
            // Passing tenant ID to the sample auth provider to use as a cache key
            var accessToken = await _authProvider.GetUserAccessTokenAsync(userId);
         ...
        }
        return _graphClient;
    }

Кажется, что происходит то, что вызов var user = await graphClient.Users.Request().GetAsync(); вызывает делегат, предоставленный GraphServiceClient. Это, в свою очередь, вызывает _authProvider.GetUserAccessTokenAsync(userId);, что приводит нас к методу public async Task<string> GetUserAccessTokenAsync(string userId). Скорее всего, наша ошибка происходит из-за отсутствия пользователей в ConfidentialClientApplication.Users коллекции

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...