Oidc-client.min.js — не пойманный (в промисе) ErrorResponse: login_required

Я использую React и IdentityServer для аутентификации. Я использую грант «код» с «PKCE». Когда мое приложение React пытается попасть в конечную точку, если оно получает 401 (НЕАВТОРИЗОВАННО), оно запускает процесс входа в систему с клиентом OIDC. Все работает очень хорошо. За исключением того, что после успешного входа в систему я вижу много ошибок в своей консоли, исходящих от oidc-client.js.

Вот видео демонстрация - https://thewikihow.com/video_vzWppuYhrKg

Это моя конфигурация IDP для этого клиента (сериализована для удобства чтения):

{
  "_id": "****",
  "Enabled": true,
  "ProtocolType": "oidc",
  "ClientSecrets": 
  [
    {
      "Value": "****",
      "Type": "SharedSecret"
    }
  ],
  "RequireClientSecret": true,
  "ClientName": "****",
  "RequireConsent": false,
  "AllowRememberConsent": true,
  "AllowedGrantTypes": 
  [
    "authorization_code",
    "client_credentials"
  ],
  "RequirePkce": true,
  "AllowPlainTextPkce": false,
  "RequireRequestObject": false,
  "AllowAccessTokensViaBrowser": false,
  "RedirectUris": 
  [
    "https://localhost:44410/callback",
    "https://authmanager.twileloop.com/callback"
  ],
  "PostLogoutRedirectUris": 
  [
    "https://localhost:44326/signout-callback-oidc"
  ],
  "FrontChannelLogoutSessionRequired": true,
  "BackChannelLogoutSessionRequired": true,
  "AllowOfflineAccess": false,
  "AllowedScopes": 
  [
    "openid",
    "read",
    "write"
  ],
  "AlwaysIncludeUserClaimsInIdToken": false,
  "IdentityTokenLifetime": 3600,
  "AllowedIdentityTokenSigningAlgorithms": [],
  "AccessTokenLifetime": 3600,
  "AuthorizationCodeLifetime": 300,
  "AbsoluteRefreshTokenLifetime": 2592000,
  "SlidingRefreshTokenLifetime": 1296000,
  "RefreshTokenUsage": "OneTimeOnly",
  "UpdateAccessTokenClaimsOnRefresh": false,
  "RefreshTokenExpiration": "Absolute",
  "AccessTokenType": "Jwt",
  "EnableLocalLogin": true,
  "IdentityProviderRestrictions": [],
  "IncludeJwtId": true,
  "Claims": [],
  "AlwaysSendClientClaims": false,
  "ClientClaimsPrefix": "client_",
  "DeviceCodeLifetime": 300,
  "AllowedCorsOrigins": 
  [
    "https://localhost:44410",
    "https://authmanager.twileloop.com"
  ],
  "Properties": {}
}

Это моя конфигурация JavaScript:

const config = {
    authority: AUTHORITY_PROD,
    client_id: '****',
    redirect_uri: ORGIN + '/callback',
    response_type: 'code',
    scope: 'openid read write',
    post_logout_redirect_uri: ORGIN + '/',
    userStore: new WebStorageStateStore({ store: window.localStorage }),
    automaticSilentRenew: false
};

const userManager = new UserManager(config);

//Login
const login = () => {
    userManager.removeUser();
    userManager.signinRedirect();
};

//Logout
const logout = () => {
    userManager.signoutRedirect();
};

ДЕМО

  1. это мое приложение

  2. В тот момент, когда я обновляюсь, он пытается попасть в конечную точку API и получает 401. Это запускает процесс аутентификации, и меня перенаправляют на «https://auth.twileloop.com», где находится мой IDP.

  3. После успешного входа в систему он перенаправляет меня обратно на URL-адрес обратного вызова, и я получаю токен, а также могу обращаться к конечным точкам API и получать данные на консоли. Ну и хорошо

  4. Если я снова обновляюсь в течение нескольких секунд, я продолжаю получать ошибки «требуется вход в систему»

Почему я получаю эту ошибку? Я обнаружил, что кто-то говорит, что политика «Chrome Same Site Cookie» вызывает проблемы. Я пробовал на Edge, но тот же результат.

Дело в том, что токен действует еще час. Я все еще могу использовать его для доступа к моему API. Срок действия токена составляет 1 час (60×60 секунд). Но эта ошибка постоянно заполняется каждый раз, когда я обновляю страницу.

  1. Кроме того, localStorage заливается

Сетевые журналы

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

Ответы 2

Ответ принят как подходящий

МОНИТОР СЕССИИ

Из вашего видео и сетевой трассировки я вижу, что используется управление сеансом OpenID Connect. Если вы посмотрите на метаданные OIDC, возвращенные с вашего сервера авторизации, вы увидите, что присутствует поле check_session_iframe URL. Этот URL возвращает некоторый код JavaScript.

Происходит то, что библиотека запускает скрытый iframe, который загружает этот код JavaScript, который затем делает Ajax-запрос к серверу авторизации каждые пару секунд, чтобы проверить состояние входа. Цель состоит в том, чтобы файл cookie SSO был отправлен в этом запросе.

Однако недавние ограничения браузера отменят файл cookie SSO, поскольку он относится к домену, отличному от исходного веб-сайта, и, таким образом, классифицируется как сторонний. Исходящий запрос включает параметр prompt=none. Поскольку файл cookie SSO отсутствует, сервер авторизации возвращает код ошибки login_required. Вы не можете полагаться на то, что этот поток работает в современных браузерах, поэтому вы должны отключить поведение sessionMonitor. См. соответствующий код библиотеки.

ГОСУДАРСТВЕННЫЙ МАГАЗИН

Если вы внимательно посмотрите на локальное хранилище, то увидите, что существует два типа записи. Тот, который содержит верификатор кода PKCE, содержит только состояние входа в систему, которое не требуется на нескольких вкладках браузера. Поэтому используйте хранилище сеансов, чтобы записи не накапливались. Это подход, который использует мой пример кода. Между тем, пользовательское хранилище — это место, где хранятся токены доступа и информация о пользователе — для этого может использоваться другой тип хранилища.

ОКОНЧАТЕЛЬНАЯ КОНФИГУРАЦИЯ

Обновите конфигурацию клиента oidc двумя новыми настройками здесь, чтобы решить обе проблемы.

const config = {
    authority: AUTHORITY_PROD,
    client_id: '****',
    redirect_uri: ORGIN + '/callback',
    response_type: 'code',
    scope: 'openid read write',
    post_logout_redirect_uri: ORGIN + '/',
    userStore: new WebStorageStateStore({ store: window.localStorage }),
    stateStore: new WebStorageStateStore({ store: window.sessionStorage }),
    automaticSilentRenew: false,
    sessionMonitor: false
};

ОТЛАДКА

Если проблемы не устранены, добавьте некоторые операторы console.info в саму клиентскую библиотеку oidc, например, отредактировав файлы в указанной ниже папке. Возможно, начнем с редактирования UserManager.js, чтобы понять, почему библиотека активировала управление сессиями:

node_modules/oidc-client-js

Привет, Гэри. Несколько пунктов, сделанных вами, - я проверил API. Он дает 401 только после истечения срока действия токена, так что эта часть проверяется. Во-вторых, как видите, я отключил Silent_login в JsConfig. У меня нет проблем с перенаправлением пользователей и входом в систему через 1 час. В то время как «automaticSilentRenew: false» попытается ли он использовать подход iframe? В-третьих, прикрепил сетевой журнал

Sangeeth Nandakumar 13.04.2023 20:59

Ах - см. последний пункт в моем обновленном ответе

Gary Archer 13.04.2023 21:15

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

Sangeeth Nandakumar 13.04.2023 21:26

Круто - я вижу, что происходит - смотрите мой обновленный ответ.

Gary Archer 13.04.2023 23:28

Спасибо Гэри. Использование предоставленной вами конфигурации по-прежнему дает те же ошибки «требуется вход» с дополнительной еще одной ошибкой - «oidc-client.min.js: 1 Uncaught (in promise) Ошибка: в хранилище на oidc-client не найдено соответствующее состояние. мин.js:1:1"

Sangeeth Nandakumar 14.04.2023 10:12

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

Gary Archer 15.04.2023 10:47

Эй, спасибо, Гэри. Оказывается, «sessionMonitor: false» делает свое дело, как вы и предлагали. Использование «sessionMonitor: false» вместе с изменением политики открытых файлов cookie на стороне моего сервера было решением. Слава !!!

Sangeeth Nandakumar 15.04.2023 22:18

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

Sangeeth Nandakumar 15.04.2023 22:32

Я получил исправление по основным моментам из ответа Гэри.

Публикация здесь, так как это будет полезно любому, кто придет сюда позже.

1. Эти изменения должны идти на стороне клиента (React, Angular, SPA..)

  1. Отключить automaticSilentRenew
  2. Отключить monitorSession

Конечная конфигурация клиента:

const config = {
    authority: AUTHORITY_PROD,
    client_id: '****',
    redirect_uri: ORGIN + '/callback',
    response_type: 'code',
    scope: 'openid read write',
    post_logout_redirect_uri: ORGIN + '/',
    userStore: new WebStorageStateStore({ store: window.localStorage }),
    automaticSilentRenew: false,
    monitorSession: false,
    client_secret: '****',
};

2. Эти изменения должны идти на стороне сервера (какая бы серверная технология не использовалась)

Я использую IdentityServer, построенный на .NET 7, так что это необходимые изменения конфигурации.

  1. Установите SameSite политику использования файлов cookie на NONE
  2. Установите SecurePolicy на ALWAYS
 services.AddIdentityServer(options => {
                //Set cookie lifetime
                options.Authentication.CookieLifetime = TimeSpan.FromSeconds(config.IdentityServerCookieLifetime);
            })
            //For local testing only
            .AddDeveloperSigningCredential()
            //Configure CORS policy
            .AddCorsPolicyService<MyCORSPolicy>()
            //Fetch OAuth v2 resources from SQLServer
            .AddResourceStore<MyResourceStore>()
            //Fetch OAuth v2 clients from SQLServer
            .AddClientStore<MyClientStore>()
            //Fetch user profiles from SQLServer
            .AddProfileService<ProfileService>();

            //Set cookie policy
            services.ConfigureApplicationCookie(options => {
                options.Cookie.SameSite = SameSiteMode.None;
                options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
            });

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