fix: 修复登录时因租户不一致导致的422策略违规错误
在连接建立时记录服务器实际使用的租户代码,而非直接从存储中读取,避免登录时的租户类型判断与会话租户不一致导致的TENANT_CODE_FORBIDDEN错误。新增登录失败后的自修复重试逻辑,当收到422错误且原因是租户代码被禁止时,临时覆盖登录类型并重试一次,同时重置相关状态避免影响后续连接。
This commit is contained in:
@@ -150,6 +150,16 @@ export function clearKickedInfo() {
|
||||
* logins (restoreSession/reconnect re-auth) are untouched -- validateLoginTenant only requires
|
||||
* login_type when scheme is "basic".
|
||||
*/
|
||||
/** The tenant the server session actually resolves to, captured when {hi} is sent (the server
|
||||
* falls back to 'default' when {hi}.tenant is absent). login_type is decided against THIS value,
|
||||
* not a fresh store read: the two decisions happen at different moments (hi on connect, login on
|
||||
* submit), and a store change in between would make login_type disagree with the session tenant,
|
||||
* which the server rejects with 422 policy violation / TENANT_CODE_FORBIDDEN -- rendered by
|
||||
* clients as "当前操作不符合安全策略要求". */
|
||||
let sessionTenantCode: string | null = null;
|
||||
/** One-shot override used by loginBasic's FORBIDDEN self-heal retry (see there). */
|
||||
let forcedLoginType: 0 | 1 | null = null;
|
||||
|
||||
function installOutgoingPacketPatches(t: Tinode) {
|
||||
const conn = t._connection;
|
||||
if (!conn || conn.__tenantPatched) return;
|
||||
@@ -160,10 +170,13 @@ function installOutgoingPacketPatches(t: Tinode) {
|
||||
const parsed = JSON.parse(msg);
|
||||
if (parsed.hi) {
|
||||
const tenant = useSessionStore.getState().tenant;
|
||||
// Whichever tenant (or absence of one) this {hi} declares, the server fixes the session
|
||||
// tenant from it right now -- so remember exactly that, for login_type decisions later.
|
||||
sessionTenantCode = (tenant?.code || 'default').trim().toLowerCase();
|
||||
if (tenant?.code) {
|
||||
parsed.hi.tenant = tenant.code;
|
||||
msg = JSON.stringify(parsed);
|
||||
}
|
||||
msg = JSON.stringify(parsed);
|
||||
}
|
||||
} catch {
|
||||
// Not valid JSON -- send as is.
|
||||
@@ -172,8 +185,12 @@ function installOutgoingPacketPatches(t: Tinode) {
|
||||
try {
|
||||
const parsed = JSON.parse(msg);
|
||||
if (parsed.login && parsed.login.scheme === 'basic') {
|
||||
const tenantCode = useSessionStore.getState().tenant?.code || 'default';
|
||||
parsed.login.login_type = tenantCode.trim().toLowerCase() === 'default' ? 1 : 0;
|
||||
if (forcedLoginType !== null) {
|
||||
parsed.login.login_type = forcedLoginType;
|
||||
} else {
|
||||
const tenantCode = sessionTenantCode ?? (useSessionStore.getState().tenant?.code || 'default');
|
||||
parsed.login.login_type = tenantCode.trim().toLowerCase() === 'default' ? 1 : 0;
|
||||
}
|
||||
msg = JSON.stringify(parsed);
|
||||
}
|
||||
} catch {
|
||||
@@ -292,6 +309,11 @@ export function resetTinode() {
|
||||
connectPromise = null;
|
||||
restorePromise = null;
|
||||
wasAuthenticated = false;
|
||||
// Both belong to the torn-down connection's session: the next getTinode() re-derives them from
|
||||
// the freshly-sent {hi}, and a stale non-null sessionTenantCode here would misclassify the very
|
||||
// first login_type of the rebuilt connection (which sends hi before any store read matters).
|
||||
sessionTenantCode = null;
|
||||
forcedLoginType = null;
|
||||
setStatus('disconnected');
|
||||
}
|
||||
|
||||
@@ -354,7 +376,24 @@ export function loginBasic(login: string, password: string): Promise<LoginCtrl>
|
||||
loginPromise = (async () => {
|
||||
const t = getTinode();
|
||||
if (!t.isConnected()) await connect();
|
||||
return t.loginBasic(login, password);
|
||||
const ctrl = await t.loginBasic(login, password);
|
||||
// Self-heal the exact failure mode observed live (server validateLoginTenant): an enterprise-
|
||||
// meaning login_type=0 against a session whose tenant is 'default' is rejected with 422 policy
|
||||
// violation, reason=TENANT_CODE_FORBIDDEN -- users see "当前操作不符合安全策略要求". That can
|
||||
// only mean the session tenant is 'default', so retry once with the personal meaning (1) before
|
||||
// giving up; a success also re-syncs sessionTenantCode so later logins on this connection don't
|
||||
// repeat the mistake.
|
||||
if (ctrl.code === 422 && (ctrl.params as { reason?: string } | undefined)?.reason === 'TENANT_CODE_FORBIDDEN') {
|
||||
forcedLoginType = 1;
|
||||
try {
|
||||
const retry = await t.loginBasic(login, password);
|
||||
if (retry.code < 300) sessionTenantCode = 'default';
|
||||
return retry;
|
||||
} finally {
|
||||
forcedLoginType = null;
|
||||
}
|
||||
}
|
||||
return ctrl;
|
||||
})().finally(() => {
|
||||
loginPromise = null;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user