Я использую 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();
};
это мое приложение
В тот момент, когда я обновляюсь, он пытается попасть в конечную точку API и получает 401. Это запускает процесс аутентификации, и меня перенаправляют на «https://auth.twileloop.com», где находится мой IDP.
После успешного входа в систему он перенаправляет меня обратно на URL-адрес обратного вызова, и я получаю токен, а также могу обращаться к конечным точкам API и получать данные на консоли. Ну и хорошо
Если я снова обновляюсь в течение нескольких секунд, я продолжаю получать ошибки «требуется вход в систему»
Почему я получаю эту ошибку? Я обнаружил, что кто-то говорит, что политика «Chrome Same Site Cookie» вызывает проблемы. Я пробовал на Edge, но тот же результат.
Дело в том, что токен действует еще час. Я все еще могу использовать его для доступа к моему API. Срок действия токена составляет 1 час (60×60 секунд). Но эта ошибка постоянно заполняется каждый раз, когда я обновляю страницу.
Сетевые журналы
МОНИТОР СЕССИИ
Из вашего видео и сетевой трассировки я вижу, что используется управление сеансом 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
Ах - см. последний пункт в моем обновленном ответе
Привет, Гэри, спасибо. Я попробовал это только сейчас, но это, похоже, не сработало. Я прикрепил короткое видео с рассматриваемой ошибкой, чтобы помочь людям лучше понять контекст.
Круто - я вижу, что происходит - смотрите мой обновленный ответ.
Спасибо Гэри. Использование предоставленной вами конфигурации по-прежнему дает те же ошибки «требуется вход» с дополнительной еще одной ошибкой - «oidc-client.min.js: 1 Uncaught (in promise) Ошибка: в хранилище на oidc-client не найдено соответствующее состояние. мин.js:1:1"
Похоже, вам нужно отладить библиотеку в качестве следующего шага, чтобы понять, почему было активировано управление сеансом. Я не могу сделать это за вас, но я добавил несколько заметок о том, как я делал это в прошлом.
Эй, спасибо, Гэри. Оказывается, «sessionMonitor: false» делает свое дело, как вы и предлагали. Использование «sessionMonitor: false» вместе с изменением политики открытых файлов cookie на стороне моего сервера было решением. Слава !!!
Добавлен дополнительный ответ с шагами, которые я выполнил, чтобы заставить его работать, на случай, если кто-то доберется сюда позже.
Я получил исправление по основным моментам из ответа Гэри.
Публикация здесь, так как это будет полезно любому, кто придет сюда позже.
automaticSilentRenew
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: '****',
};
Я использую IdentityServer, построенный на .NET 7, так что это необходимые изменения конфигурации.
SameSite
политику использования файлов cookie на NONE
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;
});
Привет, Гэри. Несколько пунктов, сделанных вами, - я проверил API. Он дает 401 только после истечения срока действия токена, так что эта часть проверяется. Во-вторых, как видите, я отключил Silent_login в JsConfig. У меня нет проблем с перенаправлением пользователей и входом в систему через 1 час. В то время как «automaticSilentRenew: false» попытается ли он использовать подход iframe? В-третьих, прикрепил сетевой журнал