Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
审计修复 Batch 2 (M4-03/M7-04/M11-01):
M4-03: 心跳引擎自动启动
- chat.rs auto-init 块: engine 创建后立即 start()
- 通过 engines.get() 获取引用避免 move 后使用
M7-04: refreshToken 发送 body 修复
- SaaSClient 新增 refreshTokenValue 存储 refresh_token
- refreshToken() 发送 { refresh_token } body
- SaaSRefreshResponse 新增 refresh_token 字段
- login/register 自动存储 refresh_token
- 添加 getRefreshToken/setRefreshToken 访问器
M11-01: blocking_lock 死锁修复 (已存在)
- 确认 try_lock + Result 匹配模式已正确
107 lines
4.0 KiB
TypeScript
107 lines
4.0 KiB
TypeScript
/**
|
|
* SaaS Auth Methods — Mixin
|
|
*
|
|
* Installs authentication-related methods onto SaaSClient.prototype.
|
|
* Uses the same mixin pattern as gateway-api.ts.
|
|
*/
|
|
|
|
import type {
|
|
SaaSAccountInfo,
|
|
SaaSLoginResponse,
|
|
SaaSRefreshResponse,
|
|
TotpSetupResponse,
|
|
TotpResultResponse,
|
|
} from './saas-types';
|
|
|
|
export function installAuthMethods(ClientClass: { prototype: any }): void {
|
|
const proto = ClientClass.prototype;
|
|
|
|
/**
|
|
* Login with username and password.
|
|
* Auto-sets the client token on success.
|
|
*/
|
|
proto.login = async function (this: { token: string | null; refreshTokenValue: string | null; request<T>(method: string, path: string, body?: unknown): Promise<T> }, username: string, password: string, totpCode?: string): Promise<SaaSLoginResponse> {
|
|
const body: Record<string, string> = { username, password };
|
|
if (totpCode) body.totp_code = totpCode; // Clear stale token before login — avoid sending expired token on auth endpoint
|
|
this.token = null;
|
|
const data = await this.request<SaaSLoginResponse>(
|
|
'POST', '/api/v1/auth/login', body,
|
|
);
|
|
this.token = data.token;
|
|
this.refreshTokenValue = data.refresh_token;
|
|
return data;
|
|
};
|
|
|
|
/**
|
|
* Register a new account.
|
|
* Auto-sets the client token on success.
|
|
*/
|
|
proto.register = async function (this: { token: string | null; refreshTokenValue: string | null; request<T>(method: string, path: string, body?: unknown): Promise<T> }, data: {
|
|
username: string;
|
|
email: string;
|
|
password: string;
|
|
display_name?: string;
|
|
}): Promise<SaaSLoginResponse> {
|
|
// Clear stale token before register
|
|
this.token = null;
|
|
const result = await this.request<SaaSLoginResponse>(
|
|
'POST', '/api/v1/auth/register', data,
|
|
);
|
|
this.token = result.token;
|
|
this.refreshTokenValue = result.refresh_token;
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* Get the current authenticated user's account info.
|
|
*/
|
|
proto.me = async function (this: { request<T>(method: string, path: string, body?: unknown): Promise<T> }): Promise<SaaSAccountInfo> {
|
|
return this.request<SaaSAccountInfo>('GET', '/api/v1/auth/me');
|
|
};
|
|
|
|
/**
|
|
* Refresh the current token.
|
|
* Auto-updates the client token on success.
|
|
*/
|
|
proto.refreshToken = async function (this: { token: string | null; refreshTokenValue: string | null; request<T>(method: string, path: string, body?: unknown): Promise<T> }): Promise<string> {
|
|
if (!this.refreshTokenValue) {
|
|
throw new Error('No refresh token available');
|
|
}
|
|
const data = await this.request<SaaSRefreshResponse>('POST', '/api/v1/auth/refresh', {
|
|
refresh_token: this.refreshTokenValue,
|
|
});
|
|
this.token = data.token;
|
|
if (data.refresh_token) {
|
|
this.refreshTokenValue = data.refresh_token;
|
|
}
|
|
return data.token;
|
|
};
|
|
|
|
/**
|
|
* Change the current user's password.
|
|
*/
|
|
proto.changePassword = async function (this: { request<T>(method: string, path: string, body?: unknown): Promise<T> }, oldPassword: string, newPassword: string): Promise<void> {
|
|
await this.request<unknown>('PUT', '/api/v1/auth/password', {
|
|
old_password: oldPassword,
|
|
new_password: newPassword,
|
|
});
|
|
};
|
|
|
|
// --- TOTP Endpoints ---
|
|
|
|
/** Generate a TOTP secret and otpauth URI */
|
|
proto.setupTotp = async function (this: { request<T>(method: string, path: string, body?: unknown): Promise<T> }): Promise<TotpSetupResponse> {
|
|
return this.request<TotpSetupResponse>('POST', '/api/v1/auth/totp/setup');
|
|
};
|
|
|
|
/** Verify a TOTP code and enable 2FA */
|
|
proto.verifyTotp = async function (this: { request<T>(method: string, path: string, body?: unknown): Promise<T> }, code: string): Promise<TotpResultResponse> {
|
|
return this.request<TotpResultResponse>('POST', '/api/v1/auth/totp/verify', { code });
|
|
};
|
|
|
|
/** Disable 2FA (requires password confirmation) */
|
|
proto.disableTotp = async function (this: { request<T>(method: string, path: string, body?: unknown): Promise<T> }, password: string): Promise<TotpResultResponse> {
|
|
return this.request<TotpResultResponse>('POST', '/api/v1/auth/totp/disable', { password });
|
|
};
|
|
}
|