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

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

Code: TokenNotFound Message: User not found in token cache. Maybe the server was restarted.

на линии

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

Вы можете помочь, что не так?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
1 731
2

Ответы 2

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

public async Task<string> GetUserAccessTokenAsync(string userId)

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

Code: TokenNotFound Message: User not found in token cache. Maybe the server was restarted.

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

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. Client challenges the user
  2. User gets redirected and logs in
  3. Callback is called and the client receives a code from the login process
  4. Pass the code to GetTokenByAuthorizationCodeAsync() to obtain an id_token and depending on the permissions an access token. GetTokenByAuthorizationCodeAsync() will store the token in the cache that has been provided to the ConfidentialClientApplication
  5. Retrieve the token from the cache with AcquireTokenSilentAsync()
  6. If we fail to retrieve a token from the cache with AcquireTokenSilentAsync(), we'll request a new one from via 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 нет пользователей.

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

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

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

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

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

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

Другие вопросы по теме