/** * 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(method: string, path: string, body?: unknown): Promise }, username: string, password: string, totpCode?: string): Promise { const body: Record = { 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( '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(method: string, path: string, body?: unknown): Promise }, data: { username: string; email: string; password: string; display_name?: string; }): Promise { // Clear stale token before register this.token = null; const result = await this.request( '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(method: string, path: string, body?: unknown): Promise }): Promise { return this.request('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(method: string, path: string, body?: unknown): Promise }): Promise { if (!this.refreshTokenValue) { throw new Error('No refresh token available'); } const data = await this.request('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(method: string, path: string, body?: unknown): Promise }, oldPassword: string, newPassword: string): Promise { await this.request('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(method: string, path: string, body?: unknown): Promise }): Promise { return this.request('POST', '/api/v1/auth/totp/setup'); }; /** Verify a TOTP code and enable 2FA */ proto.verifyTotp = async function (this: { request(method: string, path: string, body?: unknown): Promise }, code: string): Promise { return this.request('POST', '/api/v1/auth/totp/verify', { code }); }; /** Disable 2FA (requires password confirmation) */ proto.disableTotp = async function (this: { request(method: string, path: string, body?: unknown): Promise }, password: string): Promise { return this.request('POST', '/api/v1/auth/totp/disable', { password }); }; }