我正在使用 React 和 IdentityServer 进行身份验证。 我将“代码”授权与“PKCE”一起使用。 当我的 React 应用程序尝试访问端点时,如果它收到 401(未授权),它将使用 OIDC 客户端启动登录过程。一切都很好。 除了成功登录后,我在控制台中看到很多错误来自 'oidc-client.js.
这是我为此客户端的 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();
};
为什么我会收到这个错误?我发现有人说“Chrome Same Site Cookie”政策有问题。我试过 Edge 但结果相同。
事实是——Token 仍然有效一个小时。我仍然可以使用它来访问我的 API。令牌有效期为 1 小时(60×60 秒)。但是每次刷新页面时,这个错误都会不断地被填充。
会话监控器
从您的视频和网络跟踪中,我看到正在使用 OpenID Connect 会话管理。如果您查看从您的授权服务器返回的 OIDC 元数据,您将看到一个
check_session_iframe
URL 字段。此 URL 返回一些 JavaScript 代码。
发生的事情是库正在启动一个隐藏的 iframe 来加载这个 JavaScript 代码,然后每隔几秒向授权服务器发出一个 Ajax 请求,以检查登录状态。目的是在此请求中发送 SSO cookie。
然而,最近的浏览器限制将删除 SSO cookie,因为它来自不同的域到 web 源,因此被归类为第三方。传出请求包括
prompt=none
参数。由于 SSO cookie 不存在,授权服务器返回 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.log
语句添加到oidc 客户端库本身,例如通过编辑以下文件夹中的文件。也许可以从编辑 UserManager.js 开始,看看为什么库激活了会话管理:
node_modules/oidc-client-js
我从Gary's 回答的要点得出了修复。
张贴在这里,因为它会对以后来这里的人有所帮助。
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: '****',
};
我正在使用基于 .NET 7 构建的 IdentityServer,因此这些是所需的配置更改
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;
});