fix: allow oauth login without workspace payload
This commit is contained in:
@@ -182,7 +182,7 @@ export class HttpYinianControlPlane implements YinianControlPlane {
|
||||
random_str: input.randomStr,
|
||||
});
|
||||
|
||||
return this.applyLoginResponse(response);
|
||||
return this.applyLoginResponse(response, input.account);
|
||||
}
|
||||
|
||||
async logout(): Promise<YinianSessionState> {
|
||||
@@ -315,12 +315,12 @@ export class HttpYinianControlPlane implements YinianControlPlane {
|
||||
return this.storage.getSkillRegistry(hotelId);
|
||||
}
|
||||
|
||||
private async applyLoginResponse(response: JsonObject): Promise<YinianAuthSession> {
|
||||
private async applyLoginResponse(response: JsonObject, accountHint?: string): Promise<YinianAuthSession> {
|
||||
this.accessToken = readString(response, 'accessToken', 'access_token');
|
||||
this.refreshToken = readString(response, 'refreshToken', 'refresh_token');
|
||||
if (!this.accessToken) throw new Error('服务端未返回 access token');
|
||||
|
||||
const session = this.normalizeSessionFromPayload(response) ?? await this.getSessionState();
|
||||
const session = this.normalizeSessionFromPayload(response, accountHint) ?? await this.getSessionState();
|
||||
if (!session.authenticated) throw new Error('登录成功但未获取到工作空间信息');
|
||||
this.currentHotelId = session.currentHotelId;
|
||||
await this.persistSession(session);
|
||||
@@ -367,10 +367,15 @@ export class HttpYinianControlPlane implements YinianControlPlane {
|
||||
});
|
||||
}
|
||||
|
||||
private normalizeSessionFromPayload(payload: JsonObject): YinianAuthSession | null {
|
||||
const sessionPayload = readObject(payload, 'session') ?? payload;
|
||||
const userValue = readObject(sessionPayload, 'user', 'user_info', 'userInfo');
|
||||
if (!userValue) return null;
|
||||
private normalizeSessionFromPayload(payload: JsonObject, accountHint?: string): YinianAuthSession | null {
|
||||
const tokenPayload = this.accessToken ? parseJwtPayload(this.accessToken) : undefined;
|
||||
const sessionPayload = {
|
||||
...(tokenPayload ?? {}),
|
||||
...payload,
|
||||
...(readObject(payload, 'session') ?? {}),
|
||||
};
|
||||
const userValue = readObject(sessionPayload, 'user', 'user_info', 'userInfo')
|
||||
?? extractUserPayload(sessionPayload, accountHint);
|
||||
|
||||
const user = normalizeUser(userValue);
|
||||
const hotelsValue = readArray(sessionPayload, 'hotels', 'workspaces', 'businesses', 'tenants');
|
||||
@@ -516,6 +521,20 @@ function createRandomString(): string {
|
||||
return randomUUID();
|
||||
}
|
||||
|
||||
function parseJwtPayload(token: string): JsonObject | undefined {
|
||||
const [, rawPayload] = token.split('.');
|
||||
if (!rawPayload) return undefined;
|
||||
|
||||
try {
|
||||
const normalized = rawPayload.replace(/-/g, '+').replace(/_/g, '/');
|
||||
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=');
|
||||
const parsed = JSON.parse(Buffer.from(padded, 'base64').toString('utf8')) as unknown;
|
||||
return isObject(parsed) ? parsed : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function buildScopedEndpoint(endpoint: string, workspaceId: string): string {
|
||||
const [rawPath, rawQuery = ''] = endpoint.split('?');
|
||||
const path = rawPath
|
||||
@@ -563,21 +582,36 @@ function normalizeImageCaptcha(value: JsonObject, fallbackRandomStr: string): Yi
|
||||
function normalizeUser(value: unknown): YinianUser {
|
||||
const object = isObject(value) ? value : {};
|
||||
return {
|
||||
id: readString(object, 'id', 'userId', 'user_id') ?? 'user_unknown',
|
||||
name: readString(object, 'name', 'username', 'nickname') ?? '未命名用户',
|
||||
phone: readString(object, 'phone'),
|
||||
id: readString(object, 'id', 'userId', 'user_id', 'userIdStr', 'user_id_str', 'sub', 'user_name') ?? 'user_unknown',
|
||||
name: readString(object, 'name', 'username', 'userName', 'user_name', 'nickname', 'nickName', 'realName', 'real_name', 'account') ?? '未命名用户',
|
||||
phone: readString(object, 'phone', 'mobile', 'phoneNumber', 'phone_number'),
|
||||
email: readString(object, 'email'),
|
||||
avatar: readString(object, 'avatar'),
|
||||
};
|
||||
}
|
||||
|
||||
function extractUserPayload(sessionPayload: JsonObject, accountHint?: string): JsonObject {
|
||||
const account = accountHint?.trim()
|
||||
|| readString(sessionPayload, 'username', 'userName', 'user_name', 'account', 'phone', 'mobile')
|
||||
|| '当前用户';
|
||||
return {
|
||||
id: readString(sessionPayload, 'userId', 'user_id', 'id', 'sub', 'user_name') ?? account,
|
||||
name: readString(sessionPayload, 'name', 'username', 'userName', 'user_name', 'nickname', 'nickName', 'realName', 'real_name') ?? account,
|
||||
phone: readString(sessionPayload, 'phone', 'mobile', 'phoneNumber', 'phone_number'),
|
||||
email: readString(sessionPayload, 'email'),
|
||||
avatar: readString(sessionPayload, 'avatar'),
|
||||
tenantId: readString(sessionPayload, 'tenantId', 'tenant_id', 'deptId', 'dept_id', 'orgId', 'org_id'),
|
||||
};
|
||||
}
|
||||
|
||||
function createDefaultWorkspace(sessionPayload: JsonObject, userPayload: JsonObject, user: YinianUser): YinianHotel {
|
||||
const tenantId = readString(userPayload, 'tenantId', 'tenant_id')
|
||||
?? readString(sessionPayload, 'tenantId', 'tenant_id')
|
||||
?? readString(sessionPayload, 'deptId', 'dept_id', 'orgId', 'org_id')
|
||||
?? 'default';
|
||||
return {
|
||||
id: `service_${tenantId}`,
|
||||
name: readString(sessionPayload, 'tenantName', 'tenant_name', 'workspaceName', 'workspace_name')
|
||||
name: readString(sessionPayload, 'tenantName', 'tenant_name', 'workspaceName', 'workspace_name', 'deptName', 'dept_name', 'orgName', 'org_name')
|
||||
?? `${user.name}的组织空间`,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user