@timo-bank/core 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/bin/setup.js +0 -0
- package/dist/cli/setup.js +1 -1
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/setup.mjs +1 -1
- package/dist/cli/setup.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -12
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/bin/setup.js
CHANGED
|
File without changes
|
package/dist/cli/setup.js
CHANGED
|
@@ -107,7 +107,7 @@ var HttpClient = class {
|
|
|
107
107
|
* Make a GET request
|
|
108
108
|
*/
|
|
109
109
|
async get(path, options = {}) {
|
|
110
|
-
const { "content-type":
|
|
110
|
+
const { "content-type": _contentType, ...getHeaders } = DEFAULT_HEADERS;
|
|
111
111
|
const headers = {
|
|
112
112
|
...getHeaders,
|
|
113
113
|
...options.headers
|
package/dist/cli/setup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/constants.ts","../../src/errors/classes.ts","../../src/http/client.ts","../../src/utils/crypto.ts","../../src/http/headers.ts","../../src/auth/login.ts","../../src/auth/device.ts","../../src/credentials/encoder.ts","../../src/cli/setup.ts"],"names":["https","createHash","randomUUID","randomBytes","createInterface","readline"],"mappings":"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","file":"setup.js","sourcesContent":["/**\n * API configuration constants\n */\nexport const API_BASE_URL = 'https://app2.timo.vn';\nexport const API_HOSTNAME = 'app2.timo.vn';\n\n/**\n * Default browser signature format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport const DEFAULT_BROWSER_SIGNATURE = ':WEB:Windows:297:WEB:desktop:chrome';\n\n/**\n * Encryption key seed for AES encryption of device ID.\n * This is derived from Timo's public web app and is not a secret.\n * It's used to match the behavior of the official web client.\n */\nexport const ENCRYPTION_KEY_SEED = 'uuidKeyT1m0@412NTMK';\n\n/**\n * API response codes\n */\nexport const API_CODES = {\n SUCCESS: 200,\n UNAUTHORIZED: 401,\n OTP_REQUIRED: 6001,\n TWO_FACTOR_REQUIRED: 6006,\n} as const;\n\n/**\n * Default HTTP headers\n */\nexport const DEFAULT_HEADERS = {\n accept: 'application/json, text/plain, */*',\n 'content-type': 'application/json; charset=UTF-8',\n origin: 'https://my.timo.vn',\n referer: 'https://my.timo.vn/',\n 'user-agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',\n} as const;\n\n/**\n * Spend account type code\n */\nexport const ACCOUNT_TYPE_SPEND = '1025';\n","/**\n * Base error class for all Timo SDK errors\n */\nexport class TimoError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimoError';\n Object.setPrototypeOf(this, TimoError.prototype);\n }\n}\n\n/**\n * Authentication related errors\n */\nexport class AuthError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n Object.setPrototypeOf(this, AuthError.prototype);\n }\n}\n\n/**\n * Invalid or malformed credential token\n */\nexport class CredentialError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialError';\n Object.setPrototypeOf(this, CredentialError.prototype);\n }\n}\n\n/**\n * Session has expired, needs re-login\n */\nexport class SessionExpiredError extends TimoError {\n constructor(message = 'Session expired') {\n super(message);\n this.name = 'SessionExpiredError';\n Object.setPrototypeOf(this, SessionExpiredError.prototype);\n }\n}\n\n/**\n * Device has been invalidated by Timo, needs new setup\n */\nexport class DeviceInvalidatedError extends TimoError {\n constructor(message = 'Device invalidated, run setup again') {\n super(message);\n this.name = 'DeviceInvalidatedError';\n Object.setPrototypeOf(this, DeviceInvalidatedError.prototype);\n }\n}\n\n/**\n * API request failed\n */\nexport class ApiError extends TimoError {\n public readonly code: number;\n public readonly response?: unknown;\n\n constructor(message: string, code: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.code = code;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n","import * as https from 'node:https';\nimport { API_HOSTNAME, DEFAULT_HEADERS } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\nimport type { ApiResponse } from '../types/api.js';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n}\n\n/**\n * HTTP client for Timo API requests\n */\nexport class HttpClient {\n private readonly hostname: string;\n\n constructor(hostname: string = API_HOSTNAME) {\n this.hostname = hostname;\n }\n\n /**\n * Make a POST request\n */\n async post<T = unknown>(\n path: string,\n body: object,\n options: RequestOptions = {}\n ): Promise<ApiResponse<T>> {\n const data = JSON.stringify(body);\n const headers = {\n ...DEFAULT_HEADERS,\n 'content-length': Buffer.byteLength(data).toString(),\n ...options.headers,\n };\n\n return this.request<T>('POST', path, headers, data, options.timeout);\n }\n\n /**\n * Make a GET request\n */\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<ApiResponse<T>> {\n // Remove content-type for GET requests\n const { 'content-type': _, ...getHeaders } = DEFAULT_HEADERS;\n const headers = {\n ...getHeaders,\n ...options.headers,\n };\n\n return this.request<T>('GET', path, headers, undefined, options.timeout);\n }\n\n private request<T>(\n method: string,\n path: string,\n headers: Record<string, string>,\n body?: string,\n timeout = 30000\n ): Promise<ApiResponse<T>> {\n return new Promise((resolve, reject) => {\n const requestOptions = {\n hostname: this.hostname,\n port: 443,\n path,\n method,\n headers,\n timeout,\n };\n\n const req = https.request(requestOptions, (res) => {\n let responseData = '';\n\n res.on('data', (chunk) => {\n responseData += chunk;\n });\n\n res.on('end', () => {\n try {\n const parsed = JSON.parse(responseData) as ApiResponse<T>;\n resolve(parsed);\n } catch {\n reject(new ApiError('Failed to parse response', res.statusCode || 500, responseData));\n }\n });\n });\n\n req.on('error', (error) => {\n reject(new ApiError(`Request failed: ${error.message}`, 0));\n });\n\n req.on('timeout', () => {\n req.destroy();\n reject(new ApiError('Request timeout', 408));\n });\n\n if (body) {\n req.write(body);\n }\n\n req.end();\n });\n }\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { ENCRYPTION_KEY_SEED } from './constants.js';\n\n/**\n * Generate SHA256 hash\n */\nexport function sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Generate SHA512 hash\n */\nexport function sha512(input: string): string {\n return createHash('sha512').update(input).digest('hex');\n}\n\n/**\n * Get derived encryption key (first 16 chars of SHA256 of seed)\n */\nexport function getEncryptionKey(): string {\n return sha256(ENCRYPTION_KEY_SEED).substring(0, 16);\n}\n\n/**\n * Generate a cryptographically secure UUID v4\n */\nexport function generateUUID(): string {\n return randomUUID();\n}\n","import { sha256, generateUUID } from '../utils/crypto.js';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * Build device registration header value\n * Format: deviceId + browserSignature\n */\nexport function buildDeviceReg(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${deviceId}${signature}`;\n}\n\n/**\n * Build context ID header value\n * Format: sha256(WEB.deviceId.297).uuid\n */\nexport function buildContextId(deviceId: string): string {\n const inputString = `WEB.${deviceId}.297`;\n const hashPart = sha256(inputString);\n const uuid = generateUUID();\n return `${hashPart}.${uuid}`;\n}\n\n/**\n * Build device key header value (used after login)\n * Format: timoDeviceId + browserSignature\n */\nexport function buildDeviceKey(\n timoDeviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${timoDeviceId}${signature}`;\n}\n\n/**\n * Build authentication headers for API requests\n */\nexport interface AuthHeaders {\n 'x-timo-devicereg': string;\n 'x-gofs-context-id': string;\n [key: string]: string;\n}\n\nexport function buildAuthHeaders(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): AuthHeaders {\n return {\n 'x-timo-devicereg': buildDeviceReg(deviceId, signature),\n 'x-gofs-context-id': buildContextId(deviceId),\n };\n}\n\n/**\n * Build headers for authenticated requests (after login)\n */\nexport interface SessionHeaders {\n token: string;\n 'x-gofs-context-id': string;\n 'x-timo-devicekey': string;\n [key: string]: string;\n}\n\nexport function buildSessionHeaders(\n token: string,\n timoDeviceId: string,\n contextId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): SessionHeaders {\n return {\n token,\n 'x-gofs-context-id': contextId,\n 'x-timo-devicekey': buildDeviceKey(timoDeviceId, signature),\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport { buildAuthHeaders, buildContextId } from '../http/headers.js';\nimport { API_CODES } from '../utils/constants.js';\nimport { AuthError, SessionExpiredError, DeviceInvalidatedError } from '../errors/classes.js';\nimport type { ApiResponse, LoginResponseData } from '../types/api.js';\n\n/**\n * Session data after successful login\n */\nexport interface SessionData {\n token: string;\n userId: string;\n phoneNumber: string;\n timoDeviceId: string;\n contextId: string;\n}\n\n/**\n * OTP requirement response\n */\nexport interface OtpRequirement {\n type: 'device' | 'twoFactor';\n refNo: string;\n token: string;\n}\n\n/**\n * Login result - either success or OTP required\n */\nexport type LoginResult =\n | { success: true; session: SessionData }\n | { success: false; otpRequired: OtpRequirement };\n\n/**\n * Login manager for handling authentication flow\n */\nexport class LoginManager {\n private readonly httpClient: HttpClient;\n private readonly deviceId: string;\n private readonly browserSignature: string;\n private readonly contextId: string;\n\n constructor(\n deviceId: string,\n browserSignature: string,\n httpClient: HttpClient = new HttpClient()\n ) {\n this.deviceId = deviceId;\n this.browserSignature = browserSignature;\n this.httpClient = httpClient;\n this.contextId = buildContextId(deviceId);\n }\n\n /**\n * Perform initial login\n * @param username - Phone number\n * @param password - Pre-hashed password (SHA512)\n */\n async login(username: string, password: string): Promise<LoginResult> {\n const authHeaders = buildAuthHeaders(this.deviceId, this.browserSignature);\n\n const response = await this.httpClient.post<LoginResponseData>(\n '/login',\n {\n username,\n password,\n lang: 'vn',\n },\n { headers: authHeaders }\n );\n\n return this.handleLoginResponse(response);\n }\n\n /**\n * Verify device OTP (code 6001)\n */\n async verifyDeviceOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n /**\n * Verify two-factor OTP (code 6006)\n */\n async verifyTwoFactorOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/afs/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n smsOTP: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n private handleLoginResponse(response: ApiResponse<LoginResponseData>): LoginResult {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return {\n success: true,\n session: this.buildSessionData(response.data),\n };\n }\n\n if (response.code === API_CODES.OTP_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'device',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n if (response.code === API_CODES.TWO_FACTOR_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'twoFactor',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n // Handle 401 - Invalid credentials\n if (response.code === API_CODES.UNAUTHORIZED) {\n throw new AuthError('Invalid phone number or password');\n }\n\n throw new AuthError(response.message || 'Login failed');\n }\n\n private handleOtpResponse(response: ApiResponse<LoginResponseData>): SessionData {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return this.buildSessionData(response.data);\n }\n\n // Check for device invalidation\n if (response.message?.includes('device') && response.message?.includes('invalid')) {\n throw new DeviceInvalidatedError();\n }\n\n // Check for session expiry\n if (response.message?.includes('session') && response.message?.includes('expired')) {\n throw new SessionExpiredError();\n }\n\n throw new AuthError(response.message || 'OTP verification failed');\n }\n\n private buildSessionData(data: LoginResponseData): SessionData {\n if (!data.timoDeviceId) {\n throw new AuthError('Missing timoDeviceId in response');\n }\n\n return {\n token: data.token,\n userId: data.userId,\n phoneNumber: data.phoneNumber,\n timoDeviceId: data.timoDeviceId,\n contextId: this.contextId,\n };\n }\n\n /**\n * Get the context ID for this login session\n */\n getContextId(): string {\n return this.contextId;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * OS detection for browser signature\n */\nfunction getOSName(): string {\n const platform = process.platform;\n switch (platform) {\n case 'win32':\n return 'Windows';\n case 'darwin':\n return 'MacOS';\n case 'linux':\n return 'Linux';\n default:\n return 'Windows';\n }\n}\n\n/**\n * Browser name based on OS\n */\nfunction getBrowserName(): string {\n const os = getOSName();\n return os === 'MacOS' ? 'safari' : 'chrome';\n}\n\n/**\n * Generate a device fingerprint ID\n */\nexport function generateDeviceId(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * Build browser signature for current platform\n * Format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport function buildBrowserSignature(): string {\n const os = getOSName();\n const browser = getBrowserName();\n return `:WEB:${os}:297:WEB:desktop:${browser}`;\n}\n\n/**\n * Get default browser signature (Windows Chrome)\n */\nexport function getDefaultBrowserSignature(): string {\n return DEFAULT_BROWSER_SIGNATURE;\n}\n\n/**\n * Device credentials used for authentication\n */\nexport interface DeviceCredentials {\n deviceId: string;\n browserSignature: string;\n}\n\n/**\n * Generate new device credentials\n */\nexport function generateDeviceCredentials(): DeviceCredentials {\n return {\n deviceId: generateDeviceId(),\n browserSignature: buildBrowserSignature(),\n };\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\n\nexport const TOKEN_PREFIX = 'timo_v1_';\nexport const TOKEN_VERSION = 'v1';\n\n/**\n * Encode credential data to a token string.\n * Note: Base64 encoding is NOT encryption - tokens should be treated as secrets.\n */\nexport function encodeCredentials(data: CredentialData): string {\n const json = JSON.stringify(data);\n const base64 = Buffer.from(json, 'utf-8').toString('base64');\n return `${TOKEN_PREFIX}${base64}`;\n}\n\n/**\n * Decode a credential token to credential data\n */\nexport function decodeCredentials(token: string): CredentialData {\n if (!token.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError('Invalid credential token format');\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n const data = JSON.parse(json) as CredentialData;\n\n // Validate all required fields\n if (\n !data.username ||\n !data.password ||\n !data.deviceId ||\n !data.timoDeviceId ||\n !data.browserSignature ||\n !data.createdAt\n ) {\n throw new CredentialError('Invalid credential data: missing required fields');\n }\n\n return data;\n } catch (error) {\n if (error instanceof CredentialError) {\n throw error;\n }\n throw new CredentialError('Failed to decode credential token');\n }\n}\n\n/**\n * Validate a credential token format without fully decoding\n */\nexport function isValidTokenFormat(token: string): boolean {\n if (!token.startsWith(TOKEN_PREFIX)) {\n return false;\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n JSON.parse(json);\n return true;\n } catch {\n return false;\n }\n}\n","import * as readline from 'node:readline';\nimport { LoginManager } from '../auth/login.js';\nimport { generateDeviceCredentials } from '../auth/device.js';\nimport { encodeCredentials } from '../credentials/encoder.js';\nimport { sha512 } from '../utils/crypto.js';\nimport type { CredentialData } from '../types/config.js';\n\n/**\n * Create a readline interface for user input\n */\nfunction createInterface(): readline.Interface {\n return readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n}\n\n/**\n * Prompt user for input\n */\nasync function prompt(rl: readline.Interface, question: string): Promise<string> {\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Prompt for password with masking (shows * for each character)\n */\nasync function promptPassword(question: string): Promise<string> {\n return new Promise((resolve) => {\n process.stdout.write(question);\n\n let password = '';\n\n // Use raw mode for character-by-character input\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n }\n process.stdin.resume();\n process.stdin.setEncoding('utf8');\n\n const onData = (char: string): void => {\n char = char.toString();\n\n switch (char) {\n case '\\n':\n case '\\r':\n case '\\u0004': // Ctrl+D\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n console.log(); // New line\n resolve(password);\n break;\n case '\\u0003': // Ctrl+C\n console.log();\n process.exit();\n break;\n case '\\u007F': // Backspace\n case '\\b':\n if (password.length > 0) {\n password = password.slice(0, -1);\n process.stdout.write('\\b \\b'); // Erase last *\n }\n break;\n default:\n if (char.charCodeAt(0) >= 32) { // Printable characters only\n password += char;\n process.stdout.write('*');\n }\n break;\n }\n };\n\n process.stdin.on('data', onData);\n });\n}\n\n/**\n * Print colored output\n */\nfunction print(message: string, type: 'info' | 'success' | 'error' | 'warn' = 'info'): void {\n const colors = {\n info: '\\x1b[36m', // Cyan\n success: '\\x1b[32m', // Green\n error: '\\x1b[31m', // Red\n warn: '\\x1b[33m', // Yellow\n reset: '\\x1b[0m',\n };\n\n console.log(`${colors[type]}${message}${colors.reset}`);\n}\n\n/**\n * Run the setup CLI\n */\nexport async function runSetup(): Promise<void> {\n const rl = createInterface();\n\n console.log('\\n');\n print('=================================', 'info');\n print(' Timo Bank SDK Setup', 'info');\n print('=================================', 'info');\n console.log('\\n');\n\n print('This tool will register your device and generate a credential token.', 'info');\n print('The token should be stored securely as an environment variable.\\n', 'warn');\n\n try {\n // Get user credentials\n const username = await prompt(rl, 'Phone number: ');\n if (!username) {\n print('Phone number is required', 'error');\n rl.close();\n process.exit(1);\n }\n\n rl.close(); // Close readline for password input\n const password = await promptPassword('Password: ');\n if (!password) {\n print('Password is required', 'error');\n process.exit(1);\n }\n\n // Generate device credentials\n print('\\nGenerating device credentials...', 'info');\n const deviceCreds = generateDeviceCredentials();\n\n // Hash password\n const hashedPassword = sha512(password);\n\n // Create login manager\n const loginManager = new LoginManager(\n deviceCreds.deviceId,\n deviceCreds.browserSignature\n );\n\n // Attempt login\n print('Connecting to Timo Bank...', 'info');\n const loginResult = await loginManager.login(username, hashedPassword);\n\n if (loginResult.success) {\n // Direct login success (rare for new device)\n const credentialData: CredentialData = {\n username,\n password: hashedPassword,\n deviceId: deviceCreds.deviceId,\n timoDeviceId: loginResult.session.timoDeviceId,\n browserSignature: deviceCreds.browserSignature,\n createdAt: new Date().toISOString(),\n };\n\n outputCredentials(credentialData);\n } else {\n // OTP required\n print('\\nOTP sent to your registered phone or email', 'success');\n\n const rlOtp = createInterface();\n const otp = await prompt(rlOtp, 'Enter OTP: ');\n rlOtp.close();\n\n if (!otp) {\n print('OTP is required', 'error');\n process.exit(1);\n }\n\n print('\\nVerifying OTP...', 'info');\n\n let session;\n if (loginResult.otpRequired.type === 'device') {\n session = await loginManager.verifyDeviceOtp(\n otp,\n loginResult.otpRequired.refNo,\n loginResult.otpRequired.token\n );\n } else {\n session = await loginManager.verifyTwoFactorOtp(\n otp,\n loginResult.otpRequired.refNo,\n loginResult.otpRequired.token\n );\n }\n\n const credentialData: CredentialData = {\n username,\n password: hashedPassword,\n deviceId: deviceCreds.deviceId,\n timoDeviceId: session.timoDeviceId,\n browserSignature: deviceCreds.browserSignature,\n createdAt: new Date().toISOString(),\n };\n\n outputCredentials(credentialData);\n }\n } catch (error) {\n print(`\\nError: ${error instanceof Error ? error.message : 'Unknown error'}`, 'error');\n process.exit(1);\n }\n}\n\n/**\n * Output the credentials token\n */\nfunction outputCredentials(data: CredentialData): void {\n const token = encodeCredentials(data);\n\n console.log('\\n');\n print('=================================', 'success');\n print(' Setup Complete!', 'success');\n print('=================================', 'success');\n console.log('\\n');\n\n print('Add this to your .env file:\\n', 'info');\n console.log(`TIMO_CREDENTIALS=${token}`);\n console.log('\\n');\n\n print('Usage example:', 'info');\n console.log(`\nimport { TimoClient } from '@timo-bank/core';\n\nconst client = new TimoClient({\n credentials: process.env.TIMO_CREDENTIALS!,\n});\n\nawait client.login();\nconst balance = await client.getBalance();\nconsole.log(balance);\n`);\n\n print('Important: Keep your credentials token secure!', 'warn');\n console.log('\\n');\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/constants.ts","../../src/errors/classes.ts","../../src/http/client.ts","../../src/utils/crypto.ts","../../src/http/headers.ts","../../src/auth/login.ts","../../src/auth/device.ts","../../src/credentials/encoder.ts","../../src/cli/setup.ts"],"names":["https","createHash","randomUUID","randomBytes","createInterface","readline"],"mappings":"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","file":"setup.js","sourcesContent":["/**\n * API configuration constants\n */\nexport const API_BASE_URL = 'https://app2.timo.vn';\nexport const API_HOSTNAME = 'app2.timo.vn';\n\n/**\n * Default browser signature format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport const DEFAULT_BROWSER_SIGNATURE = ':WEB:Windows:297:WEB:desktop:chrome';\n\n/**\n * Encryption key seed for AES encryption of device ID.\n * This is derived from Timo's public web app and is not a secret.\n * It's used to match the behavior of the official web client.\n */\nexport const ENCRYPTION_KEY_SEED = 'uuidKeyT1m0@412NTMK';\n\n/**\n * API response codes\n */\nexport const API_CODES = {\n SUCCESS: 200,\n UNAUTHORIZED: 401,\n OTP_REQUIRED: 6001,\n TWO_FACTOR_REQUIRED: 6006,\n} as const;\n\n/**\n * Default HTTP headers\n */\nexport const DEFAULT_HEADERS = {\n accept: 'application/json, text/plain, */*',\n 'content-type': 'application/json; charset=UTF-8',\n origin: 'https://my.timo.vn',\n referer: 'https://my.timo.vn/',\n 'user-agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',\n} as const;\n\n/**\n * Spend account type code\n */\nexport const ACCOUNT_TYPE_SPEND = '1025';\n","/**\n * Base error class for all Timo SDK errors\n */\nexport class TimoError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimoError';\n Object.setPrototypeOf(this, TimoError.prototype);\n }\n}\n\n/**\n * Authentication related errors\n */\nexport class AuthError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n Object.setPrototypeOf(this, AuthError.prototype);\n }\n}\n\n/**\n * Invalid or malformed credential token\n */\nexport class CredentialError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialError';\n Object.setPrototypeOf(this, CredentialError.prototype);\n }\n}\n\n/**\n * Session has expired, needs re-login\n */\nexport class SessionExpiredError extends TimoError {\n constructor(message = 'Session expired') {\n super(message);\n this.name = 'SessionExpiredError';\n Object.setPrototypeOf(this, SessionExpiredError.prototype);\n }\n}\n\n/**\n * Device has been invalidated by Timo, needs new setup\n */\nexport class DeviceInvalidatedError extends TimoError {\n constructor(message = 'Device invalidated, run setup again') {\n super(message);\n this.name = 'DeviceInvalidatedError';\n Object.setPrototypeOf(this, DeviceInvalidatedError.prototype);\n }\n}\n\n/**\n * API request failed\n */\nexport class ApiError extends TimoError {\n public readonly code: number;\n public readonly response?: unknown;\n\n constructor(message: string, code: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.code = code;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n","import * as https from 'node:https';\nimport { API_HOSTNAME, DEFAULT_HEADERS } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\nimport type { ApiResponse } from '../types/api.js';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n}\n\n/**\n * HTTP client for Timo API requests\n */\nexport class HttpClient {\n private readonly hostname: string;\n\n constructor(hostname: string = API_HOSTNAME) {\n this.hostname = hostname;\n }\n\n /**\n * Make a POST request\n */\n async post<T = unknown>(\n path: string,\n body: object,\n options: RequestOptions = {}\n ): Promise<ApiResponse<T>> {\n const data = JSON.stringify(body);\n const headers = {\n ...DEFAULT_HEADERS,\n 'content-length': Buffer.byteLength(data).toString(),\n ...options.headers,\n };\n\n return this.request<T>('POST', path, headers, data, options.timeout);\n }\n\n /**\n * Make a GET request\n */\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<ApiResponse<T>> {\n // Remove content-type for GET requests\n const { 'content-type': _contentType, ...getHeaders } = DEFAULT_HEADERS;\n const headers = {\n ...getHeaders,\n ...options.headers,\n };\n\n return this.request<T>('GET', path, headers, undefined, options.timeout);\n }\n\n private request<T>(\n method: string,\n path: string,\n headers: Record<string, string>,\n body?: string,\n timeout = 30000\n ): Promise<ApiResponse<T>> {\n return new Promise((resolve, reject) => {\n const requestOptions = {\n hostname: this.hostname,\n port: 443,\n path,\n method,\n headers,\n timeout,\n };\n\n const req = https.request(requestOptions, (res) => {\n let responseData = '';\n\n res.on('data', (chunk) => {\n responseData += chunk;\n });\n\n res.on('end', () => {\n try {\n const parsed = JSON.parse(responseData) as ApiResponse<T>;\n resolve(parsed);\n } catch {\n reject(new ApiError('Failed to parse response', res.statusCode || 500, responseData));\n }\n });\n });\n\n req.on('error', (error) => {\n reject(new ApiError(`Request failed: ${error.message}`, 0));\n });\n\n req.on('timeout', () => {\n req.destroy();\n reject(new ApiError('Request timeout', 408));\n });\n\n if (body) {\n req.write(body);\n }\n\n req.end();\n });\n }\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { ENCRYPTION_KEY_SEED } from './constants.js';\n\n/**\n * Generate SHA256 hash\n */\nexport function sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Generate SHA512 hash\n */\nexport function sha512(input: string): string {\n return createHash('sha512').update(input).digest('hex');\n}\n\n/**\n * Get derived encryption key (first 16 chars of SHA256 of seed)\n */\nexport function getEncryptionKey(): string {\n return sha256(ENCRYPTION_KEY_SEED).substring(0, 16);\n}\n\n/**\n * Generate a cryptographically secure UUID v4\n */\nexport function generateUUID(): string {\n return randomUUID();\n}\n","import { sha256, generateUUID } from '../utils/crypto.js';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * Build device registration header value\n * Format: deviceId + browserSignature\n */\nexport function buildDeviceReg(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${deviceId}${signature}`;\n}\n\n/**\n * Build context ID header value\n * Format: sha256(WEB.deviceId.297).uuid\n */\nexport function buildContextId(deviceId: string): string {\n const inputString = `WEB.${deviceId}.297`;\n const hashPart = sha256(inputString);\n const uuid = generateUUID();\n return `${hashPart}.${uuid}`;\n}\n\n/**\n * Build device key header value (used after login)\n * Format: timoDeviceId + browserSignature\n */\nexport function buildDeviceKey(\n timoDeviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${timoDeviceId}${signature}`;\n}\n\n/**\n * Build authentication headers for API requests\n */\nexport interface AuthHeaders {\n 'x-timo-devicereg': string;\n 'x-gofs-context-id': string;\n [key: string]: string;\n}\n\nexport function buildAuthHeaders(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): AuthHeaders {\n return {\n 'x-timo-devicereg': buildDeviceReg(deviceId, signature),\n 'x-gofs-context-id': buildContextId(deviceId),\n };\n}\n\n/**\n * Build headers for authenticated requests (after login)\n */\nexport interface SessionHeaders {\n token: string;\n 'x-gofs-context-id': string;\n 'x-timo-devicekey': string;\n [key: string]: string;\n}\n\nexport function buildSessionHeaders(\n token: string,\n timoDeviceId: string,\n contextId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): SessionHeaders {\n return {\n token,\n 'x-gofs-context-id': contextId,\n 'x-timo-devicekey': buildDeviceKey(timoDeviceId, signature),\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport { buildAuthHeaders, buildContextId } from '../http/headers.js';\nimport { API_CODES } from '../utils/constants.js';\nimport { AuthError, SessionExpiredError, DeviceInvalidatedError } from '../errors/classes.js';\nimport type { ApiResponse, LoginResponseData } from '../types/api.js';\n\n/**\n * Session data after successful login\n */\nexport interface SessionData {\n token: string;\n userId: string;\n phoneNumber: string;\n timoDeviceId: string;\n contextId: string;\n}\n\n/**\n * OTP requirement response\n */\nexport interface OtpRequirement {\n type: 'device' | 'twoFactor';\n refNo: string;\n token: string;\n}\n\n/**\n * Login result - either success or OTP required\n */\nexport type LoginResult =\n | { success: true; session: SessionData }\n | { success: false; otpRequired: OtpRequirement };\n\n/**\n * Login manager for handling authentication flow\n */\nexport class LoginManager {\n private readonly httpClient: HttpClient;\n private readonly deviceId: string;\n private readonly browserSignature: string;\n private readonly contextId: string;\n\n constructor(\n deviceId: string,\n browserSignature: string,\n httpClient: HttpClient = new HttpClient()\n ) {\n this.deviceId = deviceId;\n this.browserSignature = browserSignature;\n this.httpClient = httpClient;\n this.contextId = buildContextId(deviceId);\n }\n\n /**\n * Perform initial login\n * @param username - Phone number\n * @param password - Pre-hashed password (SHA512)\n */\n async login(username: string, password: string): Promise<LoginResult> {\n const authHeaders = buildAuthHeaders(this.deviceId, this.browserSignature);\n\n const response = await this.httpClient.post<LoginResponseData>(\n '/login',\n {\n username,\n password,\n lang: 'vn',\n },\n { headers: authHeaders }\n );\n\n return this.handleLoginResponse(response);\n }\n\n /**\n * Verify device OTP (code 6001)\n */\n async verifyDeviceOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n /**\n * Verify two-factor OTP (code 6006)\n */\n async verifyTwoFactorOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/afs/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n smsOTP: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n private handleLoginResponse(response: ApiResponse<LoginResponseData>): LoginResult {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return {\n success: true,\n session: this.buildSessionData(response.data),\n };\n }\n\n if (response.code === API_CODES.OTP_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'device',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n if (response.code === API_CODES.TWO_FACTOR_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'twoFactor',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n // Handle 401 - Invalid credentials\n if (response.code === API_CODES.UNAUTHORIZED) {\n throw new AuthError('Invalid phone number or password');\n }\n\n throw new AuthError(response.message || 'Login failed');\n }\n\n private handleOtpResponse(response: ApiResponse<LoginResponseData>): SessionData {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return this.buildSessionData(response.data);\n }\n\n // Check for device invalidation\n if (response.message?.includes('device') && response.message?.includes('invalid')) {\n throw new DeviceInvalidatedError();\n }\n\n // Check for session expiry\n if (response.message?.includes('session') && response.message?.includes('expired')) {\n throw new SessionExpiredError();\n }\n\n throw new AuthError(response.message || 'OTP verification failed');\n }\n\n private buildSessionData(data: LoginResponseData): SessionData {\n if (!data.timoDeviceId) {\n throw new AuthError('Missing timoDeviceId in response');\n }\n\n return {\n token: data.token,\n userId: data.userId,\n phoneNumber: data.phoneNumber,\n timoDeviceId: data.timoDeviceId,\n contextId: this.contextId,\n };\n }\n\n /**\n * Get the context ID for this login session\n */\n getContextId(): string {\n return this.contextId;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * OS detection for browser signature\n */\nfunction getOSName(): string {\n const platform = process.platform;\n switch (platform) {\n case 'win32':\n return 'Windows';\n case 'darwin':\n return 'MacOS';\n case 'linux':\n return 'Linux';\n default:\n return 'Windows';\n }\n}\n\n/**\n * Browser name based on OS\n */\nfunction getBrowserName(): string {\n const os = getOSName();\n return os === 'MacOS' ? 'safari' : 'chrome';\n}\n\n/**\n * Generate a device fingerprint ID\n */\nexport function generateDeviceId(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * Build browser signature for current platform\n * Format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport function buildBrowserSignature(): string {\n const os = getOSName();\n const browser = getBrowserName();\n return `:WEB:${os}:297:WEB:desktop:${browser}`;\n}\n\n/**\n * Get default browser signature (Windows Chrome)\n */\nexport function getDefaultBrowserSignature(): string {\n return DEFAULT_BROWSER_SIGNATURE;\n}\n\n/**\n * Device credentials used for authentication\n */\nexport interface DeviceCredentials {\n deviceId: string;\n browserSignature: string;\n}\n\n/**\n * Generate new device credentials\n */\nexport function generateDeviceCredentials(): DeviceCredentials {\n return {\n deviceId: generateDeviceId(),\n browserSignature: buildBrowserSignature(),\n };\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\n\nexport const TOKEN_PREFIX = 'timo_v1_';\nexport const TOKEN_VERSION = 'v1';\n\n/**\n * Encode credential data to a token string.\n * Note: Base64 encoding is NOT encryption - tokens should be treated as secrets.\n */\nexport function encodeCredentials(data: CredentialData): string {\n const json = JSON.stringify(data);\n const base64 = Buffer.from(json, 'utf-8').toString('base64');\n return `${TOKEN_PREFIX}${base64}`;\n}\n\n/**\n * Decode a credential token to credential data\n */\nexport function decodeCredentials(token: string): CredentialData {\n if (!token.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError('Invalid credential token format');\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n const data = JSON.parse(json) as CredentialData;\n\n // Validate all required fields\n if (\n !data.username ||\n !data.password ||\n !data.deviceId ||\n !data.timoDeviceId ||\n !data.browserSignature ||\n !data.createdAt\n ) {\n throw new CredentialError('Invalid credential data: missing required fields');\n }\n\n return data;\n } catch (error) {\n if (error instanceof CredentialError) {\n throw error;\n }\n throw new CredentialError('Failed to decode credential token');\n }\n}\n\n/**\n * Validate a credential token format without fully decoding\n */\nexport function isValidTokenFormat(token: string): boolean {\n if (!token.startsWith(TOKEN_PREFIX)) {\n return false;\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n JSON.parse(json);\n return true;\n } catch {\n return false;\n }\n}\n","import * as readline from 'node:readline';\nimport { LoginManager } from '../auth/login.js';\nimport { generateDeviceCredentials } from '../auth/device.js';\nimport { encodeCredentials } from '../credentials/encoder.js';\nimport { sha512 } from '../utils/crypto.js';\nimport type { CredentialData } from '../types/config.js';\n\n/**\n * Create a readline interface for user input\n */\nfunction createInterface(): readline.Interface {\n return readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n}\n\n/**\n * Prompt user for input\n */\nasync function prompt(rl: readline.Interface, question: string): Promise<string> {\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Prompt for password with masking (shows * for each character)\n */\nasync function promptPassword(question: string): Promise<string> {\n return new Promise((resolve) => {\n process.stdout.write(question);\n\n let password = '';\n\n // Use raw mode for character-by-character input\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n }\n process.stdin.resume();\n process.stdin.setEncoding('utf8');\n\n const onData = (char: string): void => {\n char = char.toString();\n\n switch (char) {\n case '\\n':\n case '\\r':\n case '\\u0004': // Ctrl+D\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n console.log(); // New line\n resolve(password);\n break;\n case '\\u0003': // Ctrl+C\n console.log();\n process.exit();\n break;\n case '\\u007F': // Backspace\n case '\\b':\n if (password.length > 0) {\n password = password.slice(0, -1);\n process.stdout.write('\\b \\b'); // Erase last *\n }\n break;\n default:\n if (char.charCodeAt(0) >= 32) { // Printable characters only\n password += char;\n process.stdout.write('*');\n }\n break;\n }\n };\n\n process.stdin.on('data', onData);\n });\n}\n\n/**\n * Print colored output\n */\nfunction print(message: string, type: 'info' | 'success' | 'error' | 'warn' = 'info'): void {\n const colors = {\n info: '\\x1b[36m', // Cyan\n success: '\\x1b[32m', // Green\n error: '\\x1b[31m', // Red\n warn: '\\x1b[33m', // Yellow\n reset: '\\x1b[0m',\n };\n\n console.log(`${colors[type]}${message}${colors.reset}`);\n}\n\n/**\n * Run the setup CLI\n */\nexport async function runSetup(): Promise<void> {\n const rl = createInterface();\n\n console.log('\\n');\n print('=================================', 'info');\n print(' Timo Bank SDK Setup', 'info');\n print('=================================', 'info');\n console.log('\\n');\n\n print('This tool will register your device and generate a credential token.', 'info');\n print('The token should be stored securely as an environment variable.\\n', 'warn');\n\n try {\n // Get user credentials\n const username = await prompt(rl, 'Phone number: ');\n if (!username) {\n print('Phone number is required', 'error');\n rl.close();\n process.exit(1);\n }\n\n rl.close(); // Close readline for password input\n const password = await promptPassword('Password: ');\n if (!password) {\n print('Password is required', 'error');\n process.exit(1);\n }\n\n // Generate device credentials\n print('\\nGenerating device credentials...', 'info');\n const deviceCreds = generateDeviceCredentials();\n\n // Hash password\n const hashedPassword = sha512(password);\n\n // Create login manager\n const loginManager = new LoginManager(\n deviceCreds.deviceId,\n deviceCreds.browserSignature\n );\n\n // Attempt login\n print('Connecting to Timo Bank...', 'info');\n const loginResult = await loginManager.login(username, hashedPassword);\n\n if (loginResult.success) {\n // Direct login success (rare for new device)\n const credentialData: CredentialData = {\n username,\n password: hashedPassword,\n deviceId: deviceCreds.deviceId,\n timoDeviceId: loginResult.session.timoDeviceId,\n browserSignature: deviceCreds.browserSignature,\n createdAt: new Date().toISOString(),\n };\n\n outputCredentials(credentialData);\n } else {\n // OTP required\n print('\\nOTP sent to your registered phone or email', 'success');\n\n const rlOtp = createInterface();\n const otp = await prompt(rlOtp, 'Enter OTP: ');\n rlOtp.close();\n\n if (!otp) {\n print('OTP is required', 'error');\n process.exit(1);\n }\n\n print('\\nVerifying OTP...', 'info');\n\n let session;\n if (loginResult.otpRequired.type === 'device') {\n session = await loginManager.verifyDeviceOtp(\n otp,\n loginResult.otpRequired.refNo,\n loginResult.otpRequired.token\n );\n } else {\n session = await loginManager.verifyTwoFactorOtp(\n otp,\n loginResult.otpRequired.refNo,\n loginResult.otpRequired.token\n );\n }\n\n const credentialData: CredentialData = {\n username,\n password: hashedPassword,\n deviceId: deviceCreds.deviceId,\n timoDeviceId: session.timoDeviceId,\n browserSignature: deviceCreds.browserSignature,\n createdAt: new Date().toISOString(),\n };\n\n outputCredentials(credentialData);\n }\n } catch (error) {\n print(`\\nError: ${error instanceof Error ? error.message : 'Unknown error'}`, 'error');\n process.exit(1);\n }\n}\n\n/**\n * Output the credentials token\n */\nfunction outputCredentials(data: CredentialData): void {\n const token = encodeCredentials(data);\n\n console.log('\\n');\n print('=================================', 'success');\n print(' Setup Complete!', 'success');\n print('=================================', 'success');\n console.log('\\n');\n\n print('Add this to your .env file:\\n', 'info');\n console.log(`TIMO_CREDENTIALS=${token}`);\n console.log('\\n');\n\n print('Usage example:', 'info');\n console.log(`\nimport { TimoClient } from '@timo-bank/core';\n\nconst client = new TimoClient({\n credentials: process.env.TIMO_CREDENTIALS!,\n});\n\nawait client.login();\nconst balance = await client.getBalance();\nconsole.log(balance);\n`);\n\n print('Important: Keep your credentials token secure!', 'warn');\n console.log('\\n');\n}\n"]}
|
package/dist/cli/setup.mjs
CHANGED
|
@@ -84,7 +84,7 @@ var HttpClient = class {
|
|
|
84
84
|
* Make a GET request
|
|
85
85
|
*/
|
|
86
86
|
async get(path, options = {}) {
|
|
87
|
-
const { "content-type":
|
|
87
|
+
const { "content-type": _contentType, ...getHeaders } = DEFAULT_HEADERS;
|
|
88
88
|
const headers = {
|
|
89
89
|
...getHeaders,
|
|
90
90
|
...options.headers
|
package/dist/cli/setup.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/constants.ts","../../src/errors/classes.ts","../../src/http/client.ts","../../src/utils/crypto.ts","../../src/http/headers.ts","../../src/auth/login.ts","../../src/auth/device.ts","../../src/credentials/encoder.ts","../../src/cli/setup.ts"],"names":["createInterface"],"mappings":"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","file":"setup.mjs","sourcesContent":["/**\n * API configuration constants\n */\nexport const API_BASE_URL = 'https://app2.timo.vn';\nexport const API_HOSTNAME = 'app2.timo.vn';\n\n/**\n * Default browser signature format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport const DEFAULT_BROWSER_SIGNATURE = ':WEB:Windows:297:WEB:desktop:chrome';\n\n/**\n * Encryption key seed for AES encryption of device ID.\n * This is derived from Timo's public web app and is not a secret.\n * It's used to match the behavior of the official web client.\n */\nexport const ENCRYPTION_KEY_SEED = 'uuidKeyT1m0@412NTMK';\n\n/**\n * API response codes\n */\nexport const API_CODES = {\n SUCCESS: 200,\n UNAUTHORIZED: 401,\n OTP_REQUIRED: 6001,\n TWO_FACTOR_REQUIRED: 6006,\n} as const;\n\n/**\n * Default HTTP headers\n */\nexport const DEFAULT_HEADERS = {\n accept: 'application/json, text/plain, */*',\n 'content-type': 'application/json; charset=UTF-8',\n origin: 'https://my.timo.vn',\n referer: 'https://my.timo.vn/',\n 'user-agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',\n} as const;\n\n/**\n * Spend account type code\n */\nexport const ACCOUNT_TYPE_SPEND = '1025';\n","/**\n * Base error class for all Timo SDK errors\n */\nexport class TimoError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimoError';\n Object.setPrototypeOf(this, TimoError.prototype);\n }\n}\n\n/**\n * Authentication related errors\n */\nexport class AuthError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n Object.setPrototypeOf(this, AuthError.prototype);\n }\n}\n\n/**\n * Invalid or malformed credential token\n */\nexport class CredentialError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialError';\n Object.setPrototypeOf(this, CredentialError.prototype);\n }\n}\n\n/**\n * Session has expired, needs re-login\n */\nexport class SessionExpiredError extends TimoError {\n constructor(message = 'Session expired') {\n super(message);\n this.name = 'SessionExpiredError';\n Object.setPrototypeOf(this, SessionExpiredError.prototype);\n }\n}\n\n/**\n * Device has been invalidated by Timo, needs new setup\n */\nexport class DeviceInvalidatedError extends TimoError {\n constructor(message = 'Device invalidated, run setup again') {\n super(message);\n this.name = 'DeviceInvalidatedError';\n Object.setPrototypeOf(this, DeviceInvalidatedError.prototype);\n }\n}\n\n/**\n * API request failed\n */\nexport class ApiError extends TimoError {\n public readonly code: number;\n public readonly response?: unknown;\n\n constructor(message: string, code: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.code = code;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n","import * as https from 'node:https';\nimport { API_HOSTNAME, DEFAULT_HEADERS } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\nimport type { ApiResponse } from '../types/api.js';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n}\n\n/**\n * HTTP client for Timo API requests\n */\nexport class HttpClient {\n private readonly hostname: string;\n\n constructor(hostname: string = API_HOSTNAME) {\n this.hostname = hostname;\n }\n\n /**\n * Make a POST request\n */\n async post<T = unknown>(\n path: string,\n body: object,\n options: RequestOptions = {}\n ): Promise<ApiResponse<T>> {\n const data = JSON.stringify(body);\n const headers = {\n ...DEFAULT_HEADERS,\n 'content-length': Buffer.byteLength(data).toString(),\n ...options.headers,\n };\n\n return this.request<T>('POST', path, headers, data, options.timeout);\n }\n\n /**\n * Make a GET request\n */\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<ApiResponse<T>> {\n // Remove content-type for GET requests\n const { 'content-type': _, ...getHeaders } = DEFAULT_HEADERS;\n const headers = {\n ...getHeaders,\n ...options.headers,\n };\n\n return this.request<T>('GET', path, headers, undefined, options.timeout);\n }\n\n private request<T>(\n method: string,\n path: string,\n headers: Record<string, string>,\n body?: string,\n timeout = 30000\n ): Promise<ApiResponse<T>> {\n return new Promise((resolve, reject) => {\n const requestOptions = {\n hostname: this.hostname,\n port: 443,\n path,\n method,\n headers,\n timeout,\n };\n\n const req = https.request(requestOptions, (res) => {\n let responseData = '';\n\n res.on('data', (chunk) => {\n responseData += chunk;\n });\n\n res.on('end', () => {\n try {\n const parsed = JSON.parse(responseData) as ApiResponse<T>;\n resolve(parsed);\n } catch {\n reject(new ApiError('Failed to parse response', res.statusCode || 500, responseData));\n }\n });\n });\n\n req.on('error', (error) => {\n reject(new ApiError(`Request failed: ${error.message}`, 0));\n });\n\n req.on('timeout', () => {\n req.destroy();\n reject(new ApiError('Request timeout', 408));\n });\n\n if (body) {\n req.write(body);\n }\n\n req.end();\n });\n }\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { ENCRYPTION_KEY_SEED } from './constants.js';\n\n/**\n * Generate SHA256 hash\n */\nexport function sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Generate SHA512 hash\n */\nexport function sha512(input: string): string {\n return createHash('sha512').update(input).digest('hex');\n}\n\n/**\n * Get derived encryption key (first 16 chars of SHA256 of seed)\n */\nexport function getEncryptionKey(): string {\n return sha256(ENCRYPTION_KEY_SEED).substring(0, 16);\n}\n\n/**\n * Generate a cryptographically secure UUID v4\n */\nexport function generateUUID(): string {\n return randomUUID();\n}\n","import { sha256, generateUUID } from '../utils/crypto.js';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * Build device registration header value\n * Format: deviceId + browserSignature\n */\nexport function buildDeviceReg(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${deviceId}${signature}`;\n}\n\n/**\n * Build context ID header value\n * Format: sha256(WEB.deviceId.297).uuid\n */\nexport function buildContextId(deviceId: string): string {\n const inputString = `WEB.${deviceId}.297`;\n const hashPart = sha256(inputString);\n const uuid = generateUUID();\n return `${hashPart}.${uuid}`;\n}\n\n/**\n * Build device key header value (used after login)\n * Format: timoDeviceId + browserSignature\n */\nexport function buildDeviceKey(\n timoDeviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${timoDeviceId}${signature}`;\n}\n\n/**\n * Build authentication headers for API requests\n */\nexport interface AuthHeaders {\n 'x-timo-devicereg': string;\n 'x-gofs-context-id': string;\n [key: string]: string;\n}\n\nexport function buildAuthHeaders(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): AuthHeaders {\n return {\n 'x-timo-devicereg': buildDeviceReg(deviceId, signature),\n 'x-gofs-context-id': buildContextId(deviceId),\n };\n}\n\n/**\n * Build headers for authenticated requests (after login)\n */\nexport interface SessionHeaders {\n token: string;\n 'x-gofs-context-id': string;\n 'x-timo-devicekey': string;\n [key: string]: string;\n}\n\nexport function buildSessionHeaders(\n token: string,\n timoDeviceId: string,\n contextId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): SessionHeaders {\n return {\n token,\n 'x-gofs-context-id': contextId,\n 'x-timo-devicekey': buildDeviceKey(timoDeviceId, signature),\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport { buildAuthHeaders, buildContextId } from '../http/headers.js';\nimport { API_CODES } from '../utils/constants.js';\nimport { AuthError, SessionExpiredError, DeviceInvalidatedError } from '../errors/classes.js';\nimport type { ApiResponse, LoginResponseData } from '../types/api.js';\n\n/**\n * Session data after successful login\n */\nexport interface SessionData {\n token: string;\n userId: string;\n phoneNumber: string;\n timoDeviceId: string;\n contextId: string;\n}\n\n/**\n * OTP requirement response\n */\nexport interface OtpRequirement {\n type: 'device' | 'twoFactor';\n refNo: string;\n token: string;\n}\n\n/**\n * Login result - either success or OTP required\n */\nexport type LoginResult =\n | { success: true; session: SessionData }\n | { success: false; otpRequired: OtpRequirement };\n\n/**\n * Login manager for handling authentication flow\n */\nexport class LoginManager {\n private readonly httpClient: HttpClient;\n private readonly deviceId: string;\n private readonly browserSignature: string;\n private readonly contextId: string;\n\n constructor(\n deviceId: string,\n browserSignature: string,\n httpClient: HttpClient = new HttpClient()\n ) {\n this.deviceId = deviceId;\n this.browserSignature = browserSignature;\n this.httpClient = httpClient;\n this.contextId = buildContextId(deviceId);\n }\n\n /**\n * Perform initial login\n * @param username - Phone number\n * @param password - Pre-hashed password (SHA512)\n */\n async login(username: string, password: string): Promise<LoginResult> {\n const authHeaders = buildAuthHeaders(this.deviceId, this.browserSignature);\n\n const response = await this.httpClient.post<LoginResponseData>(\n '/login',\n {\n username,\n password,\n lang: 'vn',\n },\n { headers: authHeaders }\n );\n\n return this.handleLoginResponse(response);\n }\n\n /**\n * Verify device OTP (code 6001)\n */\n async verifyDeviceOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n /**\n * Verify two-factor OTP (code 6006)\n */\n async verifyTwoFactorOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/afs/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n smsOTP: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n private handleLoginResponse(response: ApiResponse<LoginResponseData>): LoginResult {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return {\n success: true,\n session: this.buildSessionData(response.data),\n };\n }\n\n if (response.code === API_CODES.OTP_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'device',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n if (response.code === API_CODES.TWO_FACTOR_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'twoFactor',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n // Handle 401 - Invalid credentials\n if (response.code === API_CODES.UNAUTHORIZED) {\n throw new AuthError('Invalid phone number or password');\n }\n\n throw new AuthError(response.message || 'Login failed');\n }\n\n private handleOtpResponse(response: ApiResponse<LoginResponseData>): SessionData {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return this.buildSessionData(response.data);\n }\n\n // Check for device invalidation\n if (response.message?.includes('device') && response.message?.includes('invalid')) {\n throw new DeviceInvalidatedError();\n }\n\n // Check for session expiry\n if (response.message?.includes('session') && response.message?.includes('expired')) {\n throw new SessionExpiredError();\n }\n\n throw new AuthError(response.message || 'OTP verification failed');\n }\n\n private buildSessionData(data: LoginResponseData): SessionData {\n if (!data.timoDeviceId) {\n throw new AuthError('Missing timoDeviceId in response');\n }\n\n return {\n token: data.token,\n userId: data.userId,\n phoneNumber: data.phoneNumber,\n timoDeviceId: data.timoDeviceId,\n contextId: this.contextId,\n };\n }\n\n /**\n * Get the context ID for this login session\n */\n getContextId(): string {\n return this.contextId;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * OS detection for browser signature\n */\nfunction getOSName(): string {\n const platform = process.platform;\n switch (platform) {\n case 'win32':\n return 'Windows';\n case 'darwin':\n return 'MacOS';\n case 'linux':\n return 'Linux';\n default:\n return 'Windows';\n }\n}\n\n/**\n * Browser name based on OS\n */\nfunction getBrowserName(): string {\n const os = getOSName();\n return os === 'MacOS' ? 'safari' : 'chrome';\n}\n\n/**\n * Generate a device fingerprint ID\n */\nexport function generateDeviceId(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * Build browser signature for current platform\n * Format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport function buildBrowserSignature(): string {\n const os = getOSName();\n const browser = getBrowserName();\n return `:WEB:${os}:297:WEB:desktop:${browser}`;\n}\n\n/**\n * Get default browser signature (Windows Chrome)\n */\nexport function getDefaultBrowserSignature(): string {\n return DEFAULT_BROWSER_SIGNATURE;\n}\n\n/**\n * Device credentials used for authentication\n */\nexport interface DeviceCredentials {\n deviceId: string;\n browserSignature: string;\n}\n\n/**\n * Generate new device credentials\n */\nexport function generateDeviceCredentials(): DeviceCredentials {\n return {\n deviceId: generateDeviceId(),\n browserSignature: buildBrowserSignature(),\n };\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\n\nexport const TOKEN_PREFIX = 'timo_v1_';\nexport const TOKEN_VERSION = 'v1';\n\n/**\n * Encode credential data to a token string.\n * Note: Base64 encoding is NOT encryption - tokens should be treated as secrets.\n */\nexport function encodeCredentials(data: CredentialData): string {\n const json = JSON.stringify(data);\n const base64 = Buffer.from(json, 'utf-8').toString('base64');\n return `${TOKEN_PREFIX}${base64}`;\n}\n\n/**\n * Decode a credential token to credential data\n */\nexport function decodeCredentials(token: string): CredentialData {\n if (!token.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError('Invalid credential token format');\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n const data = JSON.parse(json) as CredentialData;\n\n // Validate all required fields\n if (\n !data.username ||\n !data.password ||\n !data.deviceId ||\n !data.timoDeviceId ||\n !data.browserSignature ||\n !data.createdAt\n ) {\n throw new CredentialError('Invalid credential data: missing required fields');\n }\n\n return data;\n } catch (error) {\n if (error instanceof CredentialError) {\n throw error;\n }\n throw new CredentialError('Failed to decode credential token');\n }\n}\n\n/**\n * Validate a credential token format without fully decoding\n */\nexport function isValidTokenFormat(token: string): boolean {\n if (!token.startsWith(TOKEN_PREFIX)) {\n return false;\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n JSON.parse(json);\n return true;\n } catch {\n return false;\n }\n}\n","import * as readline from 'node:readline';\nimport { LoginManager } from '../auth/login.js';\nimport { generateDeviceCredentials } from '../auth/device.js';\nimport { encodeCredentials } from '../credentials/encoder.js';\nimport { sha512 } from '../utils/crypto.js';\nimport type { CredentialData } from '../types/config.js';\n\n/**\n * Create a readline interface for user input\n */\nfunction createInterface(): readline.Interface {\n return readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n}\n\n/**\n * Prompt user for input\n */\nasync function prompt(rl: readline.Interface, question: string): Promise<string> {\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Prompt for password with masking (shows * for each character)\n */\nasync function promptPassword(question: string): Promise<string> {\n return new Promise((resolve) => {\n process.stdout.write(question);\n\n let password = '';\n\n // Use raw mode for character-by-character input\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n }\n process.stdin.resume();\n process.stdin.setEncoding('utf8');\n\n const onData = (char: string): void => {\n char = char.toString();\n\n switch (char) {\n case '\\n':\n case '\\r':\n case '\\u0004': // Ctrl+D\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n console.log(); // New line\n resolve(password);\n break;\n case '\\u0003': // Ctrl+C\n console.log();\n process.exit();\n break;\n case '\\u007F': // Backspace\n case '\\b':\n if (password.length > 0) {\n password = password.slice(0, -1);\n process.stdout.write('\\b \\b'); // Erase last *\n }\n break;\n default:\n if (char.charCodeAt(0) >= 32) { // Printable characters only\n password += char;\n process.stdout.write('*');\n }\n break;\n }\n };\n\n process.stdin.on('data', onData);\n });\n}\n\n/**\n * Print colored output\n */\nfunction print(message: string, type: 'info' | 'success' | 'error' | 'warn' = 'info'): void {\n const colors = {\n info: '\\x1b[36m', // Cyan\n success: '\\x1b[32m', // Green\n error: '\\x1b[31m', // Red\n warn: '\\x1b[33m', // Yellow\n reset: '\\x1b[0m',\n };\n\n console.log(`${colors[type]}${message}${colors.reset}`);\n}\n\n/**\n * Run the setup CLI\n */\nexport async function runSetup(): Promise<void> {\n const rl = createInterface();\n\n console.log('\\n');\n print('=================================', 'info');\n print(' Timo Bank SDK Setup', 'info');\n print('=================================', 'info');\n console.log('\\n');\n\n print('This tool will register your device and generate a credential token.', 'info');\n print('The token should be stored securely as an environment variable.\\n', 'warn');\n\n try {\n // Get user credentials\n const username = await prompt(rl, 'Phone number: ');\n if (!username) {\n print('Phone number is required', 'error');\n rl.close();\n process.exit(1);\n }\n\n rl.close(); // Close readline for password input\n const password = await promptPassword('Password: ');\n if (!password) {\n print('Password is required', 'error');\n process.exit(1);\n }\n\n // Generate device credentials\n print('\\nGenerating device credentials...', 'info');\n const deviceCreds = generateDeviceCredentials();\n\n // Hash password\n const hashedPassword = sha512(password);\n\n // Create login manager\n const loginManager = new LoginManager(\n deviceCreds.deviceId,\n deviceCreds.browserSignature\n );\n\n // Attempt login\n print('Connecting to Timo Bank...', 'info');\n const loginResult = await loginManager.login(username, hashedPassword);\n\n if (loginResult.success) {\n // Direct login success (rare for new device)\n const credentialData: CredentialData = {\n username,\n password: hashedPassword,\n deviceId: deviceCreds.deviceId,\n timoDeviceId: loginResult.session.timoDeviceId,\n browserSignature: deviceCreds.browserSignature,\n createdAt: new Date().toISOString(),\n };\n\n outputCredentials(credentialData);\n } else {\n // OTP required\n print('\\nOTP sent to your registered phone or email', 'success');\n\n const rlOtp = createInterface();\n const otp = await prompt(rlOtp, 'Enter OTP: ');\n rlOtp.close();\n\n if (!otp) {\n print('OTP is required', 'error');\n process.exit(1);\n }\n\n print('\\nVerifying OTP...', 'info');\n\n let session;\n if (loginResult.otpRequired.type === 'device') {\n session = await loginManager.verifyDeviceOtp(\n otp,\n loginResult.otpRequired.refNo,\n loginResult.otpRequired.token\n );\n } else {\n session = await loginManager.verifyTwoFactorOtp(\n otp,\n loginResult.otpRequired.refNo,\n loginResult.otpRequired.token\n );\n }\n\n const credentialData: CredentialData = {\n username,\n password: hashedPassword,\n deviceId: deviceCreds.deviceId,\n timoDeviceId: session.timoDeviceId,\n browserSignature: deviceCreds.browserSignature,\n createdAt: new Date().toISOString(),\n };\n\n outputCredentials(credentialData);\n }\n } catch (error) {\n print(`\\nError: ${error instanceof Error ? error.message : 'Unknown error'}`, 'error');\n process.exit(1);\n }\n}\n\n/**\n * Output the credentials token\n */\nfunction outputCredentials(data: CredentialData): void {\n const token = encodeCredentials(data);\n\n console.log('\\n');\n print('=================================', 'success');\n print(' Setup Complete!', 'success');\n print('=================================', 'success');\n console.log('\\n');\n\n print('Add this to your .env file:\\n', 'info');\n console.log(`TIMO_CREDENTIALS=${token}`);\n console.log('\\n');\n\n print('Usage example:', 'info');\n console.log(`\nimport { TimoClient } from '@timo-bank/core';\n\nconst client = new TimoClient({\n credentials: process.env.TIMO_CREDENTIALS!,\n});\n\nawait client.login();\nconst balance = await client.getBalance();\nconsole.log(balance);\n`);\n\n print('Important: Keep your credentials token secure!', 'warn');\n console.log('\\n');\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/constants.ts","../../src/errors/classes.ts","../../src/http/client.ts","../../src/utils/crypto.ts","../../src/http/headers.ts","../../src/auth/login.ts","../../src/auth/device.ts","../../src/credentials/encoder.ts","../../src/cli/setup.ts"],"names":["createInterface"],"mappings":"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","file":"setup.mjs","sourcesContent":["/**\n * API configuration constants\n */\nexport const API_BASE_URL = 'https://app2.timo.vn';\nexport const API_HOSTNAME = 'app2.timo.vn';\n\n/**\n * Default browser signature format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport const DEFAULT_BROWSER_SIGNATURE = ':WEB:Windows:297:WEB:desktop:chrome';\n\n/**\n * Encryption key seed for AES encryption of device ID.\n * This is derived from Timo's public web app and is not a secret.\n * It's used to match the behavior of the official web client.\n */\nexport const ENCRYPTION_KEY_SEED = 'uuidKeyT1m0@412NTMK';\n\n/**\n * API response codes\n */\nexport const API_CODES = {\n SUCCESS: 200,\n UNAUTHORIZED: 401,\n OTP_REQUIRED: 6001,\n TWO_FACTOR_REQUIRED: 6006,\n} as const;\n\n/**\n * Default HTTP headers\n */\nexport const DEFAULT_HEADERS = {\n accept: 'application/json, text/plain, */*',\n 'content-type': 'application/json; charset=UTF-8',\n origin: 'https://my.timo.vn',\n referer: 'https://my.timo.vn/',\n 'user-agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',\n} as const;\n\n/**\n * Spend account type code\n */\nexport const ACCOUNT_TYPE_SPEND = '1025';\n","/**\n * Base error class for all Timo SDK errors\n */\nexport class TimoError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimoError';\n Object.setPrototypeOf(this, TimoError.prototype);\n }\n}\n\n/**\n * Authentication related errors\n */\nexport class AuthError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n Object.setPrototypeOf(this, AuthError.prototype);\n }\n}\n\n/**\n * Invalid or malformed credential token\n */\nexport class CredentialError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialError';\n Object.setPrototypeOf(this, CredentialError.prototype);\n }\n}\n\n/**\n * Session has expired, needs re-login\n */\nexport class SessionExpiredError extends TimoError {\n constructor(message = 'Session expired') {\n super(message);\n this.name = 'SessionExpiredError';\n Object.setPrototypeOf(this, SessionExpiredError.prototype);\n }\n}\n\n/**\n * Device has been invalidated by Timo, needs new setup\n */\nexport class DeviceInvalidatedError extends TimoError {\n constructor(message = 'Device invalidated, run setup again') {\n super(message);\n this.name = 'DeviceInvalidatedError';\n Object.setPrototypeOf(this, DeviceInvalidatedError.prototype);\n }\n}\n\n/**\n * API request failed\n */\nexport class ApiError extends TimoError {\n public readonly code: number;\n public readonly response?: unknown;\n\n constructor(message: string, code: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.code = code;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n","import * as https from 'node:https';\nimport { API_HOSTNAME, DEFAULT_HEADERS } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\nimport type { ApiResponse } from '../types/api.js';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n}\n\n/**\n * HTTP client for Timo API requests\n */\nexport class HttpClient {\n private readonly hostname: string;\n\n constructor(hostname: string = API_HOSTNAME) {\n this.hostname = hostname;\n }\n\n /**\n * Make a POST request\n */\n async post<T = unknown>(\n path: string,\n body: object,\n options: RequestOptions = {}\n ): Promise<ApiResponse<T>> {\n const data = JSON.stringify(body);\n const headers = {\n ...DEFAULT_HEADERS,\n 'content-length': Buffer.byteLength(data).toString(),\n ...options.headers,\n };\n\n return this.request<T>('POST', path, headers, data, options.timeout);\n }\n\n /**\n * Make a GET request\n */\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<ApiResponse<T>> {\n // Remove content-type for GET requests\n const { 'content-type': _contentType, ...getHeaders } = DEFAULT_HEADERS;\n const headers = {\n ...getHeaders,\n ...options.headers,\n };\n\n return this.request<T>('GET', path, headers, undefined, options.timeout);\n }\n\n private request<T>(\n method: string,\n path: string,\n headers: Record<string, string>,\n body?: string,\n timeout = 30000\n ): Promise<ApiResponse<T>> {\n return new Promise((resolve, reject) => {\n const requestOptions = {\n hostname: this.hostname,\n port: 443,\n path,\n method,\n headers,\n timeout,\n };\n\n const req = https.request(requestOptions, (res) => {\n let responseData = '';\n\n res.on('data', (chunk) => {\n responseData += chunk;\n });\n\n res.on('end', () => {\n try {\n const parsed = JSON.parse(responseData) as ApiResponse<T>;\n resolve(parsed);\n } catch {\n reject(new ApiError('Failed to parse response', res.statusCode || 500, responseData));\n }\n });\n });\n\n req.on('error', (error) => {\n reject(new ApiError(`Request failed: ${error.message}`, 0));\n });\n\n req.on('timeout', () => {\n req.destroy();\n reject(new ApiError('Request timeout', 408));\n });\n\n if (body) {\n req.write(body);\n }\n\n req.end();\n });\n }\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { ENCRYPTION_KEY_SEED } from './constants.js';\n\n/**\n * Generate SHA256 hash\n */\nexport function sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Generate SHA512 hash\n */\nexport function sha512(input: string): string {\n return createHash('sha512').update(input).digest('hex');\n}\n\n/**\n * Get derived encryption key (first 16 chars of SHA256 of seed)\n */\nexport function getEncryptionKey(): string {\n return sha256(ENCRYPTION_KEY_SEED).substring(0, 16);\n}\n\n/**\n * Generate a cryptographically secure UUID v4\n */\nexport function generateUUID(): string {\n return randomUUID();\n}\n","import { sha256, generateUUID } from '../utils/crypto.js';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * Build device registration header value\n * Format: deviceId + browserSignature\n */\nexport function buildDeviceReg(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${deviceId}${signature}`;\n}\n\n/**\n * Build context ID header value\n * Format: sha256(WEB.deviceId.297).uuid\n */\nexport function buildContextId(deviceId: string): string {\n const inputString = `WEB.${deviceId}.297`;\n const hashPart = sha256(inputString);\n const uuid = generateUUID();\n return `${hashPart}.${uuid}`;\n}\n\n/**\n * Build device key header value (used after login)\n * Format: timoDeviceId + browserSignature\n */\nexport function buildDeviceKey(\n timoDeviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${timoDeviceId}${signature}`;\n}\n\n/**\n * Build authentication headers for API requests\n */\nexport interface AuthHeaders {\n 'x-timo-devicereg': string;\n 'x-gofs-context-id': string;\n [key: string]: string;\n}\n\nexport function buildAuthHeaders(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): AuthHeaders {\n return {\n 'x-timo-devicereg': buildDeviceReg(deviceId, signature),\n 'x-gofs-context-id': buildContextId(deviceId),\n };\n}\n\n/**\n * Build headers for authenticated requests (after login)\n */\nexport interface SessionHeaders {\n token: string;\n 'x-gofs-context-id': string;\n 'x-timo-devicekey': string;\n [key: string]: string;\n}\n\nexport function buildSessionHeaders(\n token: string,\n timoDeviceId: string,\n contextId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): SessionHeaders {\n return {\n token,\n 'x-gofs-context-id': contextId,\n 'x-timo-devicekey': buildDeviceKey(timoDeviceId, signature),\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport { buildAuthHeaders, buildContextId } from '../http/headers.js';\nimport { API_CODES } from '../utils/constants.js';\nimport { AuthError, SessionExpiredError, DeviceInvalidatedError } from '../errors/classes.js';\nimport type { ApiResponse, LoginResponseData } from '../types/api.js';\n\n/**\n * Session data after successful login\n */\nexport interface SessionData {\n token: string;\n userId: string;\n phoneNumber: string;\n timoDeviceId: string;\n contextId: string;\n}\n\n/**\n * OTP requirement response\n */\nexport interface OtpRequirement {\n type: 'device' | 'twoFactor';\n refNo: string;\n token: string;\n}\n\n/**\n * Login result - either success or OTP required\n */\nexport type LoginResult =\n | { success: true; session: SessionData }\n | { success: false; otpRequired: OtpRequirement };\n\n/**\n * Login manager for handling authentication flow\n */\nexport class LoginManager {\n private readonly httpClient: HttpClient;\n private readonly deviceId: string;\n private readonly browserSignature: string;\n private readonly contextId: string;\n\n constructor(\n deviceId: string,\n browserSignature: string,\n httpClient: HttpClient = new HttpClient()\n ) {\n this.deviceId = deviceId;\n this.browserSignature = browserSignature;\n this.httpClient = httpClient;\n this.contextId = buildContextId(deviceId);\n }\n\n /**\n * Perform initial login\n * @param username - Phone number\n * @param password - Pre-hashed password (SHA512)\n */\n async login(username: string, password: string): Promise<LoginResult> {\n const authHeaders = buildAuthHeaders(this.deviceId, this.browserSignature);\n\n const response = await this.httpClient.post<LoginResponseData>(\n '/login',\n {\n username,\n password,\n lang: 'vn',\n },\n { headers: authHeaders }\n );\n\n return this.handleLoginResponse(response);\n }\n\n /**\n * Verify device OTP (code 6001)\n */\n async verifyDeviceOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n /**\n * Verify two-factor OTP (code 6006)\n */\n async verifyTwoFactorOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/afs/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n smsOTP: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n private handleLoginResponse(response: ApiResponse<LoginResponseData>): LoginResult {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return {\n success: true,\n session: this.buildSessionData(response.data),\n };\n }\n\n if (response.code === API_CODES.OTP_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'device',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n if (response.code === API_CODES.TWO_FACTOR_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'twoFactor',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n // Handle 401 - Invalid credentials\n if (response.code === API_CODES.UNAUTHORIZED) {\n throw new AuthError('Invalid phone number or password');\n }\n\n throw new AuthError(response.message || 'Login failed');\n }\n\n private handleOtpResponse(response: ApiResponse<LoginResponseData>): SessionData {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return this.buildSessionData(response.data);\n }\n\n // Check for device invalidation\n if (response.message?.includes('device') && response.message?.includes('invalid')) {\n throw new DeviceInvalidatedError();\n }\n\n // Check for session expiry\n if (response.message?.includes('session') && response.message?.includes('expired')) {\n throw new SessionExpiredError();\n }\n\n throw new AuthError(response.message || 'OTP verification failed');\n }\n\n private buildSessionData(data: LoginResponseData): SessionData {\n if (!data.timoDeviceId) {\n throw new AuthError('Missing timoDeviceId in response');\n }\n\n return {\n token: data.token,\n userId: data.userId,\n phoneNumber: data.phoneNumber,\n timoDeviceId: data.timoDeviceId,\n contextId: this.contextId,\n };\n }\n\n /**\n * Get the context ID for this login session\n */\n getContextId(): string {\n return this.contextId;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * OS detection for browser signature\n */\nfunction getOSName(): string {\n const platform = process.platform;\n switch (platform) {\n case 'win32':\n return 'Windows';\n case 'darwin':\n return 'MacOS';\n case 'linux':\n return 'Linux';\n default:\n return 'Windows';\n }\n}\n\n/**\n * Browser name based on OS\n */\nfunction getBrowserName(): string {\n const os = getOSName();\n return os === 'MacOS' ? 'safari' : 'chrome';\n}\n\n/**\n * Generate a device fingerprint ID\n */\nexport function generateDeviceId(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * Build browser signature for current platform\n * Format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport function buildBrowserSignature(): string {\n const os = getOSName();\n const browser = getBrowserName();\n return `:WEB:${os}:297:WEB:desktop:${browser}`;\n}\n\n/**\n * Get default browser signature (Windows Chrome)\n */\nexport function getDefaultBrowserSignature(): string {\n return DEFAULT_BROWSER_SIGNATURE;\n}\n\n/**\n * Device credentials used for authentication\n */\nexport interface DeviceCredentials {\n deviceId: string;\n browserSignature: string;\n}\n\n/**\n * Generate new device credentials\n */\nexport function generateDeviceCredentials(): DeviceCredentials {\n return {\n deviceId: generateDeviceId(),\n browserSignature: buildBrowserSignature(),\n };\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\n\nexport const TOKEN_PREFIX = 'timo_v1_';\nexport const TOKEN_VERSION = 'v1';\n\n/**\n * Encode credential data to a token string.\n * Note: Base64 encoding is NOT encryption - tokens should be treated as secrets.\n */\nexport function encodeCredentials(data: CredentialData): string {\n const json = JSON.stringify(data);\n const base64 = Buffer.from(json, 'utf-8').toString('base64');\n return `${TOKEN_PREFIX}${base64}`;\n}\n\n/**\n * Decode a credential token to credential data\n */\nexport function decodeCredentials(token: string): CredentialData {\n if (!token.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError('Invalid credential token format');\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n const data = JSON.parse(json) as CredentialData;\n\n // Validate all required fields\n if (\n !data.username ||\n !data.password ||\n !data.deviceId ||\n !data.timoDeviceId ||\n !data.browserSignature ||\n !data.createdAt\n ) {\n throw new CredentialError('Invalid credential data: missing required fields');\n }\n\n return data;\n } catch (error) {\n if (error instanceof CredentialError) {\n throw error;\n }\n throw new CredentialError('Failed to decode credential token');\n }\n}\n\n/**\n * Validate a credential token format without fully decoding\n */\nexport function isValidTokenFormat(token: string): boolean {\n if (!token.startsWith(TOKEN_PREFIX)) {\n return false;\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n JSON.parse(json);\n return true;\n } catch {\n return false;\n }\n}\n","import * as readline from 'node:readline';\nimport { LoginManager } from '../auth/login.js';\nimport { generateDeviceCredentials } from '../auth/device.js';\nimport { encodeCredentials } from '../credentials/encoder.js';\nimport { sha512 } from '../utils/crypto.js';\nimport type { CredentialData } from '../types/config.js';\n\n/**\n * Create a readline interface for user input\n */\nfunction createInterface(): readline.Interface {\n return readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n}\n\n/**\n * Prompt user for input\n */\nasync function prompt(rl: readline.Interface, question: string): Promise<string> {\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Prompt for password with masking (shows * for each character)\n */\nasync function promptPassword(question: string): Promise<string> {\n return new Promise((resolve) => {\n process.stdout.write(question);\n\n let password = '';\n\n // Use raw mode for character-by-character input\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n }\n process.stdin.resume();\n process.stdin.setEncoding('utf8');\n\n const onData = (char: string): void => {\n char = char.toString();\n\n switch (char) {\n case '\\n':\n case '\\r':\n case '\\u0004': // Ctrl+D\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n console.log(); // New line\n resolve(password);\n break;\n case '\\u0003': // Ctrl+C\n console.log();\n process.exit();\n break;\n case '\\u007F': // Backspace\n case '\\b':\n if (password.length > 0) {\n password = password.slice(0, -1);\n process.stdout.write('\\b \\b'); // Erase last *\n }\n break;\n default:\n if (char.charCodeAt(0) >= 32) { // Printable characters only\n password += char;\n process.stdout.write('*');\n }\n break;\n }\n };\n\n process.stdin.on('data', onData);\n });\n}\n\n/**\n * Print colored output\n */\nfunction print(message: string, type: 'info' | 'success' | 'error' | 'warn' = 'info'): void {\n const colors = {\n info: '\\x1b[36m', // Cyan\n success: '\\x1b[32m', // Green\n error: '\\x1b[31m', // Red\n warn: '\\x1b[33m', // Yellow\n reset: '\\x1b[0m',\n };\n\n console.log(`${colors[type]}${message}${colors.reset}`);\n}\n\n/**\n * Run the setup CLI\n */\nexport async function runSetup(): Promise<void> {\n const rl = createInterface();\n\n console.log('\\n');\n print('=================================', 'info');\n print(' Timo Bank SDK Setup', 'info');\n print('=================================', 'info');\n console.log('\\n');\n\n print('This tool will register your device and generate a credential token.', 'info');\n print('The token should be stored securely as an environment variable.\\n', 'warn');\n\n try {\n // Get user credentials\n const username = await prompt(rl, 'Phone number: ');\n if (!username) {\n print('Phone number is required', 'error');\n rl.close();\n process.exit(1);\n }\n\n rl.close(); // Close readline for password input\n const password = await promptPassword('Password: ');\n if (!password) {\n print('Password is required', 'error');\n process.exit(1);\n }\n\n // Generate device credentials\n print('\\nGenerating device credentials...', 'info');\n const deviceCreds = generateDeviceCredentials();\n\n // Hash password\n const hashedPassword = sha512(password);\n\n // Create login manager\n const loginManager = new LoginManager(\n deviceCreds.deviceId,\n deviceCreds.browserSignature\n );\n\n // Attempt login\n print('Connecting to Timo Bank...', 'info');\n const loginResult = await loginManager.login(username, hashedPassword);\n\n if (loginResult.success) {\n // Direct login success (rare for new device)\n const credentialData: CredentialData = {\n username,\n password: hashedPassword,\n deviceId: deviceCreds.deviceId,\n timoDeviceId: loginResult.session.timoDeviceId,\n browserSignature: deviceCreds.browserSignature,\n createdAt: new Date().toISOString(),\n };\n\n outputCredentials(credentialData);\n } else {\n // OTP required\n print('\\nOTP sent to your registered phone or email', 'success');\n\n const rlOtp = createInterface();\n const otp = await prompt(rlOtp, 'Enter OTP: ');\n rlOtp.close();\n\n if (!otp) {\n print('OTP is required', 'error');\n process.exit(1);\n }\n\n print('\\nVerifying OTP...', 'info');\n\n let session;\n if (loginResult.otpRequired.type === 'device') {\n session = await loginManager.verifyDeviceOtp(\n otp,\n loginResult.otpRequired.refNo,\n loginResult.otpRequired.token\n );\n } else {\n session = await loginManager.verifyTwoFactorOtp(\n otp,\n loginResult.otpRequired.refNo,\n loginResult.otpRequired.token\n );\n }\n\n const credentialData: CredentialData = {\n username,\n password: hashedPassword,\n deviceId: deviceCreds.deviceId,\n timoDeviceId: session.timoDeviceId,\n browserSignature: deviceCreds.browserSignature,\n createdAt: new Date().toISOString(),\n };\n\n outputCredentials(credentialData);\n }\n } catch (error) {\n print(`\\nError: ${error instanceof Error ? error.message : 'Unknown error'}`, 'error');\n process.exit(1);\n }\n}\n\n/**\n * Output the credentials token\n */\nfunction outputCredentials(data: CredentialData): void {\n const token = encodeCredentials(data);\n\n console.log('\\n');\n print('=================================', 'success');\n print(' Setup Complete!', 'success');\n print('=================================', 'success');\n console.log('\\n');\n\n print('Add this to your .env file:\\n', 'info');\n console.log(`TIMO_CREDENTIALS=${token}`);\n console.log('\\n');\n\n print('Usage example:', 'info');\n console.log(`\nimport { TimoClient } from '@timo-bank/core';\n\nconst client = new TimoClient({\n credentials: process.env.TIMO_CREDENTIALS!,\n});\n\nawait client.login();\nconst balance = await client.getBalance();\nconsole.log(balance);\n`);\n\n print('Important: Keep your credentials token secure!', 'warn');\n console.log('\\n');\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -113,7 +113,7 @@ var HttpClient = class {
|
|
|
113
113
|
* Make a GET request
|
|
114
114
|
*/
|
|
115
115
|
async get(path, options = {}) {
|
|
116
|
-
const { "content-type":
|
|
116
|
+
const { "content-type": _contentType, ...getHeaders } = DEFAULT_HEADERS;
|
|
117
117
|
const headers = {
|
|
118
118
|
...getHeaders,
|
|
119
119
|
...options.headers
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/constants.ts","../src/errors/classes.ts","../src/http/client.ts","../src/utils/crypto.ts","../src/http/headers.ts","../src/auth/login.ts","../src/credentials/encoder.ts","../src/credentials/parser.ts","../src/api/balance.ts","../src/api/transaction.ts","../src/api/account.ts","../src/client.ts","../src/auth/device.ts","../src/index.ts"],"names":["https","createHash","randomUUID","formatDate","randomBytes"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * API configuration constants\n */\nexport const API_BASE_URL = 'https://app2.timo.vn';\nexport const API_HOSTNAME = 'app2.timo.vn';\n\n/**\n * Default browser signature format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport const DEFAULT_BROWSER_SIGNATURE = ':WEB:Windows:297:WEB:desktop:chrome';\n\n/**\n * Encryption key seed for AES encryption of device ID.\n * This is derived from Timo's public web app and is not a secret.\n * It's used to match the behavior of the official web client.\n */\nexport const ENCRYPTION_KEY_SEED = 'uuidKeyT1m0@412NTMK';\n\n/**\n * API response codes\n */\nexport const API_CODES = {\n SUCCESS: 200,\n UNAUTHORIZED: 401,\n OTP_REQUIRED: 6001,\n TWO_FACTOR_REQUIRED: 6006,\n} as const;\n\n/**\n * Default HTTP headers\n */\nexport const DEFAULT_HEADERS = {\n accept: 'application/json, text/plain, */*',\n 'content-type': 'application/json; charset=UTF-8',\n origin: 'https://my.timo.vn',\n referer: 'https://my.timo.vn/',\n 'user-agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',\n} as const;\n\n/**\n * Spend account type code\n */\nexport const ACCOUNT_TYPE_SPEND = '1025';\n","/**\n * Base error class for all Timo SDK errors\n */\nexport class TimoError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimoError';\n Object.setPrototypeOf(this, TimoError.prototype);\n }\n}\n\n/**\n * Authentication related errors\n */\nexport class AuthError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n Object.setPrototypeOf(this, AuthError.prototype);\n }\n}\n\n/**\n * Invalid or malformed credential token\n */\nexport class CredentialError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialError';\n Object.setPrototypeOf(this, CredentialError.prototype);\n }\n}\n\n/**\n * Session has expired, needs re-login\n */\nexport class SessionExpiredError extends TimoError {\n constructor(message = 'Session expired') {\n super(message);\n this.name = 'SessionExpiredError';\n Object.setPrototypeOf(this, SessionExpiredError.prototype);\n }\n}\n\n/**\n * Device has been invalidated by Timo, needs new setup\n */\nexport class DeviceInvalidatedError extends TimoError {\n constructor(message = 'Device invalidated, run setup again') {\n super(message);\n this.name = 'DeviceInvalidatedError';\n Object.setPrototypeOf(this, DeviceInvalidatedError.prototype);\n }\n}\n\n/**\n * API request failed\n */\nexport class ApiError extends TimoError {\n public readonly code: number;\n public readonly response?: unknown;\n\n constructor(message: string, code: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.code = code;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n","import * as https from 'node:https';\nimport { API_HOSTNAME, DEFAULT_HEADERS } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\nimport type { ApiResponse } from '../types/api.js';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n}\n\n/**\n * HTTP client for Timo API requests\n */\nexport class HttpClient {\n private readonly hostname: string;\n\n constructor(hostname: string = API_HOSTNAME) {\n this.hostname = hostname;\n }\n\n /**\n * Make a POST request\n */\n async post<T = unknown>(\n path: string,\n body: object,\n options: RequestOptions = {}\n ): Promise<ApiResponse<T>> {\n const data = JSON.stringify(body);\n const headers = {\n ...DEFAULT_HEADERS,\n 'content-length': Buffer.byteLength(data).toString(),\n ...options.headers,\n };\n\n return this.request<T>('POST', path, headers, data, options.timeout);\n }\n\n /**\n * Make a GET request\n */\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<ApiResponse<T>> {\n // Remove content-type for GET requests\n const { 'content-type': _, ...getHeaders } = DEFAULT_HEADERS;\n const headers = {\n ...getHeaders,\n ...options.headers,\n };\n\n return this.request<T>('GET', path, headers, undefined, options.timeout);\n }\n\n private request<T>(\n method: string,\n path: string,\n headers: Record<string, string>,\n body?: string,\n timeout = 30000\n ): Promise<ApiResponse<T>> {\n return new Promise((resolve, reject) => {\n const requestOptions = {\n hostname: this.hostname,\n port: 443,\n path,\n method,\n headers,\n timeout,\n };\n\n const req = https.request(requestOptions, (res) => {\n let responseData = '';\n\n res.on('data', (chunk) => {\n responseData += chunk;\n });\n\n res.on('end', () => {\n try {\n const parsed = JSON.parse(responseData) as ApiResponse<T>;\n resolve(parsed);\n } catch {\n reject(new ApiError('Failed to parse response', res.statusCode || 500, responseData));\n }\n });\n });\n\n req.on('error', (error) => {\n reject(new ApiError(`Request failed: ${error.message}`, 0));\n });\n\n req.on('timeout', () => {\n req.destroy();\n reject(new ApiError('Request timeout', 408));\n });\n\n if (body) {\n req.write(body);\n }\n\n req.end();\n });\n }\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { ENCRYPTION_KEY_SEED } from './constants.js';\n\n/**\n * Generate SHA256 hash\n */\nexport function sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Generate SHA512 hash\n */\nexport function sha512(input: string): string {\n return createHash('sha512').update(input).digest('hex');\n}\n\n/**\n * Get derived encryption key (first 16 chars of SHA256 of seed)\n */\nexport function getEncryptionKey(): string {\n return sha256(ENCRYPTION_KEY_SEED).substring(0, 16);\n}\n\n/**\n * Generate a cryptographically secure UUID v4\n */\nexport function generateUUID(): string {\n return randomUUID();\n}\n","import { sha256, generateUUID } from '../utils/crypto.js';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * Build device registration header value\n * Format: deviceId + browserSignature\n */\nexport function buildDeviceReg(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${deviceId}${signature}`;\n}\n\n/**\n * Build context ID header value\n * Format: sha256(WEB.deviceId.297).uuid\n */\nexport function buildContextId(deviceId: string): string {\n const inputString = `WEB.${deviceId}.297`;\n const hashPart = sha256(inputString);\n const uuid = generateUUID();\n return `${hashPart}.${uuid}`;\n}\n\n/**\n * Build device key header value (used after login)\n * Format: timoDeviceId + browserSignature\n */\nexport function buildDeviceKey(\n timoDeviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${timoDeviceId}${signature}`;\n}\n\n/**\n * Build authentication headers for API requests\n */\nexport interface AuthHeaders {\n 'x-timo-devicereg': string;\n 'x-gofs-context-id': string;\n [key: string]: string;\n}\n\nexport function buildAuthHeaders(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): AuthHeaders {\n return {\n 'x-timo-devicereg': buildDeviceReg(deviceId, signature),\n 'x-gofs-context-id': buildContextId(deviceId),\n };\n}\n\n/**\n * Build headers for authenticated requests (after login)\n */\nexport interface SessionHeaders {\n token: string;\n 'x-gofs-context-id': string;\n 'x-timo-devicekey': string;\n [key: string]: string;\n}\n\nexport function buildSessionHeaders(\n token: string,\n timoDeviceId: string,\n contextId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): SessionHeaders {\n return {\n token,\n 'x-gofs-context-id': contextId,\n 'x-timo-devicekey': buildDeviceKey(timoDeviceId, signature),\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport { buildAuthHeaders, buildContextId } from '../http/headers.js';\nimport { API_CODES } from '../utils/constants.js';\nimport { AuthError, SessionExpiredError, DeviceInvalidatedError } from '../errors/classes.js';\nimport type { ApiResponse, LoginResponseData } from '../types/api.js';\n\n/**\n * Session data after successful login\n */\nexport interface SessionData {\n token: string;\n userId: string;\n phoneNumber: string;\n timoDeviceId: string;\n contextId: string;\n}\n\n/**\n * OTP requirement response\n */\nexport interface OtpRequirement {\n type: 'device' | 'twoFactor';\n refNo: string;\n token: string;\n}\n\n/**\n * Login result - either success or OTP required\n */\nexport type LoginResult =\n | { success: true; session: SessionData }\n | { success: false; otpRequired: OtpRequirement };\n\n/**\n * Login manager for handling authentication flow\n */\nexport class LoginManager {\n private readonly httpClient: HttpClient;\n private readonly deviceId: string;\n private readonly browserSignature: string;\n private readonly contextId: string;\n\n constructor(\n deviceId: string,\n browserSignature: string,\n httpClient: HttpClient = new HttpClient()\n ) {\n this.deviceId = deviceId;\n this.browserSignature = browserSignature;\n this.httpClient = httpClient;\n this.contextId = buildContextId(deviceId);\n }\n\n /**\n * Perform initial login\n * @param username - Phone number\n * @param password - Pre-hashed password (SHA512)\n */\n async login(username: string, password: string): Promise<LoginResult> {\n const authHeaders = buildAuthHeaders(this.deviceId, this.browserSignature);\n\n const response = await this.httpClient.post<LoginResponseData>(\n '/login',\n {\n username,\n password,\n lang: 'vn',\n },\n { headers: authHeaders }\n );\n\n return this.handleLoginResponse(response);\n }\n\n /**\n * Verify device OTP (code 6001)\n */\n async verifyDeviceOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n /**\n * Verify two-factor OTP (code 6006)\n */\n async verifyTwoFactorOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/afs/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n smsOTP: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n private handleLoginResponse(response: ApiResponse<LoginResponseData>): LoginResult {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return {\n success: true,\n session: this.buildSessionData(response.data),\n };\n }\n\n if (response.code === API_CODES.OTP_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'device',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n if (response.code === API_CODES.TWO_FACTOR_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'twoFactor',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n // Handle 401 - Invalid credentials\n if (response.code === API_CODES.UNAUTHORIZED) {\n throw new AuthError('Invalid phone number or password');\n }\n\n throw new AuthError(response.message || 'Login failed');\n }\n\n private handleOtpResponse(response: ApiResponse<LoginResponseData>): SessionData {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return this.buildSessionData(response.data);\n }\n\n // Check for device invalidation\n if (response.message?.includes('device') && response.message?.includes('invalid')) {\n throw new DeviceInvalidatedError();\n }\n\n // Check for session expiry\n if (response.message?.includes('session') && response.message?.includes('expired')) {\n throw new SessionExpiredError();\n }\n\n throw new AuthError(response.message || 'OTP verification failed');\n }\n\n private buildSessionData(data: LoginResponseData): SessionData {\n if (!data.timoDeviceId) {\n throw new AuthError('Missing timoDeviceId in response');\n }\n\n return {\n token: data.token,\n userId: data.userId,\n phoneNumber: data.phoneNumber,\n timoDeviceId: data.timoDeviceId,\n contextId: this.contextId,\n };\n }\n\n /**\n * Get the context ID for this login session\n */\n getContextId(): string {\n return this.contextId;\n }\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\n\nexport const TOKEN_PREFIX = 'timo_v1_';\nexport const TOKEN_VERSION = 'v1';\n\n/**\n * Encode credential data to a token string.\n * Note: Base64 encoding is NOT encryption - tokens should be treated as secrets.\n */\nexport function encodeCredentials(data: CredentialData): string {\n const json = JSON.stringify(data);\n const base64 = Buffer.from(json, 'utf-8').toString('base64');\n return `${TOKEN_PREFIX}${base64}`;\n}\n\n/**\n * Decode a credential token to credential data\n */\nexport function decodeCredentials(token: string): CredentialData {\n if (!token.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError('Invalid credential token format');\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n const data = JSON.parse(json) as CredentialData;\n\n // Validate all required fields\n if (\n !data.username ||\n !data.password ||\n !data.deviceId ||\n !data.timoDeviceId ||\n !data.browserSignature ||\n !data.createdAt\n ) {\n throw new CredentialError('Invalid credential data: missing required fields');\n }\n\n return data;\n } catch (error) {\n if (error instanceof CredentialError) {\n throw error;\n }\n throw new CredentialError('Failed to decode credential token');\n }\n}\n\n/**\n * Validate a credential token format without fully decoding\n */\nexport function isValidTokenFormat(token: string): boolean {\n if (!token.startsWith(TOKEN_PREFIX)) {\n return false;\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n JSON.parse(json);\n return true;\n } catch {\n return false;\n }\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\nimport { TOKEN_PREFIX, decodeCredentials } from './encoder.js';\n\n/**\n * Extract version from token\n */\nexport function getTokenVersion(token: string): string {\n if (token.startsWith(TOKEN_PREFIX)) {\n return 'v1';\n }\n throw new CredentialError('Unsupported credential version');\n}\n\n/**\n * Parse and validate a credential token\n */\nexport function parseCredentialToken(token: string): CredentialData {\n if (!token || typeof token !== 'string') {\n throw new CredentialError('Credential token is required');\n }\n\n const trimmed = token.trim();\n\n if (!trimmed.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError(\n `Invalid credential token format. Expected token starting with \"${TOKEN_PREFIX}\"`\n );\n }\n\n // Validate version\n getTokenVersion(trimmed);\n\n // Decode and validate\n return decodeCredentials(trimmed);\n}\n\n/**\n * Mask sensitive data in credentials for safe logging\n */\nexport function maskCredentials(data: CredentialData): Record<string, string> {\n return {\n username: maskPhone(data.username),\n password: '***',\n deviceId: maskString(data.deviceId),\n timoDeviceId: maskString(data.timoDeviceId),\n browserSignature: maskBrowserSignature(data.browserSignature),\n createdAt: data.createdAt,\n };\n}\n\nfunction maskBrowserSignature(sig: string): string {\n // Browser signature format: :WEB:OS:VERSION:WEB:device:browser\n // Keep format visible but mask specific values\n const parts = sig.split(':');\n if (parts.length >= 6) {\n return `:${parts[1]}:***:***:${parts[4]}:***:***`;\n }\n return '***';\n}\n\nfunction maskPhone(phone: string): string {\n if (phone.length <= 4) return '****';\n return phone.slice(0, 4) + '*'.repeat(phone.length - 4);\n}\n\nfunction maskString(str: string): string {\n if (str.length <= 8) return '****';\n return str.slice(0, 4) + '...' + str.slice(-4);\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { Balance } from '../types/models.js';\nimport type { TransactionListResponseData } from '../types/api.js';\nimport { ACCOUNT_TYPE_SPEND } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\n\n/**\n * Format a date as DD/MM/YYYY for Timo API\n */\nfunction formatDate(date: Date): string {\n const day = String(date.getDate()).padStart(2, '0');\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const year = date.getFullYear();\n return `${day}/${month}/${year}`;\n}\n\n/**\n * Fetch account balance via transaction list endpoint\n */\nexport async function fetchBalance(\n httpClient: HttpClient,\n accountNo: string,\n sessionHeaders: SessionHeaders\n): Promise<Balance> {\n // Use transaction list endpoint to get balance info\n const today = new Date();\n const payload = {\n format: 'group',\n index: 0,\n offset: -1,\n accountNo,\n accountType: ACCOUNT_TYPE_SPEND,\n fromDate: formatDate(today),\n toDate: formatDate(today),\n };\n\n const response = await httpClient.post<TransactionListResponseData>(\n '/user/account/transaction/list',\n payload,\n { headers: sessionHeaders }\n );\n\n if (!response.success || !response.data) {\n throw new ApiError('Failed to fetch balance', response.code);\n }\n\n return {\n accountNo,\n accountBalance: response.data.accountBalance ?? 0,\n availableAmount: response.data.availableAmount ?? 0,\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { Transaction, TransactionOptions } from '../types/models.js';\nimport type { TransactionListResponseData, TransactionItem } from '../types/api.js';\nimport { ACCOUNT_TYPE_SPEND } from '../utils/constants.js';\n\n/**\n * Format a date as DD/MM/YYYY for Timo API\n */\nfunction formatDate(date: Date): string {\n const day = String(date.getDate()).padStart(2, '0');\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const year = date.getFullYear();\n return `${day}/${month}/${year}`;\n}\n\n/**\n * Get default date range (last 30 days)\n */\nfunction getDefaultDateRange(): { fromDate: string; toDate: string } {\n const today = new Date();\n const thirtyDaysAgo = new Date(today);\n thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);\n\n return {\n fromDate: formatDate(thirtyDaysAgo),\n toDate: formatDate(today),\n };\n}\n\n/**\n * Map API transaction item to Transaction model\n */\nfunction mapTransaction(item: TransactionItem): Transaction {\n return {\n refNo: item.refNo,\n txnAmount: item.txnAmount,\n txnDesc: item.txnDesc,\n txnTime: item.txnTime,\n txnType: item.txnType,\n transactionTime: item.transactionTime,\n txnBankName: item.txnBankName,\n txnBankAccount: item.txnBankAccount,\n };\n}\n\n/**\n * Fetch transaction history\n */\nexport async function fetchTransactions(\n httpClient: HttpClient,\n accountNo: string,\n sessionHeaders: SessionHeaders,\n options?: TransactionOptions\n): Promise<Transaction[]> {\n const dateRange = getDefaultDateRange();\n\n const payload = {\n format: 'group',\n index: 0,\n offset: -1,\n accountNo,\n accountType: ACCOUNT_TYPE_SPEND,\n fromDate: options?.fromDate ?? dateRange.fromDate,\n toDate: options?.toDate ?? dateRange.toDate,\n };\n\n const response = await httpClient.post<TransactionListResponseData>(\n '/user/account/transaction/list',\n payload,\n { headers: sessionHeaders }\n );\n\n if (!response.success || !response.data) {\n return [];\n }\n\n // Flatten grouped transactions\n const allTransactions: Transaction[] = [];\n for (const group of response.data.items ?? []) {\n for (const item of group.item ?? []) {\n allTransactions.push(mapTransaction(item));\n }\n }\n\n // Apply limit if specified\n if (options?.limit && options.limit > 0) {\n return allTransactions.slice(0, options.limit);\n }\n\n return allTransactions;\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { UserProfile, Account, AccountType } from '../types/models.js';\n\n/** Account type mapping from API codes */\nconst ACCOUNT_TYPE_MAP: Record<string, AccountType> = {\n '1025': 'spend',\n '1026': 'goal',\n '1027': 'term',\n '1028': 'money_market',\n '9999': 'credit',\n '1910': 'split_bill',\n '6511': 'insurance',\n};\n\ninterface AccountPreviewItem {\n no: string;\n accountType: string;\n name: string;\n balance?: number;\n accountBalance?: number;\n availableAmount?: number;\n}\n\ninterface AccountPreviewResponse {\n accounts: AccountPreviewItem[];\n}\n\ninterface ProfileResponseData {\n userId: number;\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n legalId?: string;\n cityName?: string;\n address?: string;\n birthday?: string;\n gender?: string;\n}\n\n/**\n * Fetch user profile from /user/getProfile\n * Note: accountNo is not available from this API - must be provided via options\n */\nexport async function fetchUserProfile(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<UserProfile | null> {\n const response = await httpClient.get<ProfileResponseData>('/user/getProfile', {\n headers: sessionHeaders,\n });\n\n if (!response.success || !response.data) {\n return null;\n }\n\n const data = response.data;\n const fullName = `${data.firstName} ${data.lastName}`.trim();\n\n return {\n userId: String(data.userId),\n phoneNumber: data.phone,\n fullName: fullName || undefined,\n email: data.email,\n };\n}\n\n/**\n * Fetch all accounts from /user/accountPreview\n * Returns list of all user accounts (spend, goal, term, etc.)\n */\nexport async function fetchAccounts(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<Account[]> {\n const response = await httpClient.get<AccountPreviewResponse>('/user/accountPreview', {\n headers: sessionHeaders,\n });\n\n if (!response.success || !response.data?.accounts) {\n return [];\n }\n\n return response.data.accounts\n .filter((item) => item.no) // Only include accounts with a number\n .map((item) => ({\n no: item.no,\n accountType: item.accountType,\n type: ACCOUNT_TYPE_MAP[item.accountType] ?? 'spend',\n name: item.name,\n balance: item.balance,\n accountBalance: item.accountBalance,\n availableAmount: item.availableAmount,\n }));\n}\n\n/**\n * Fetch spend account number from /user/accountPreview\n * Returns the main Spend Account number (accountType: 1025)\n */\nexport async function fetchSpendAccountNo(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<string | null> {\n const accounts = await fetchAccounts(httpClient, sessionHeaders);\n const spendAccount = accounts.find((a) => a.type === 'spend');\n return spendAccount?.no ?? null;\n}\n","import { HttpClient } from './http/client.js';\nimport { buildSessionHeaders } from './http/headers.js';\nimport { LoginManager } from './auth/login.js';\nimport type { SessionData } from './auth/login.js';\nimport { parseCredentialToken } from './credentials/parser.js';\nimport { fetchBalance } from './api/balance.js';\nimport { fetchTransactions } from './api/transaction.js';\nimport { fetchUserProfile, fetchSpendAccountNo } from './api/account.js';\nimport type { TimoClientOptions, CredentialData, Logger } from './types/config.js';\nimport type { Balance, Transaction, TransactionOptions, AccountInfo, UserProfile } from './types/models.js';\nimport { AuthError } from './errors/classes.js';\n\n/**\n * Main Timo Bank client\n */\nexport class TimoClient {\n private readonly credentials: CredentialData;\n private readonly httpClient: HttpClient;\n private readonly logger?: Logger;\n private session: SessionData | null = null;\n private accountNo: string | null = null;\n\n constructor(options: TimoClientOptions) {\n this.credentials = parseCredentialToken(options.credentials);\n this.httpClient = new HttpClient();\n this.logger = options.logger;\n this.accountNo = options.accountNo ?? null;\n }\n\n /**\n * Login to Timo Bank\n * Uses saved device credentials for automatic login without OTP\n */\n async login(): Promise<void> {\n const loginManager = new LoginManager(\n this.credentials.deviceId,\n this.credentials.browserSignature,\n this.httpClient\n );\n\n // Login using saved credentials (should not require OTP with valid timoDeviceId)\n const result = await loginManager.login(this.credentials.username, this.credentials.password);\n\n if (!result.success) {\n throw new AuthError(\n `Device requires re-authorization. OTP type: ${result.otpRequired.type}. ` +\n 'Run \"npx @timo-bank/core setup\" to re-authorize this device.'\n );\n }\n\n this.session = result.session;\n this.logger?.info('Login successful', { userId: this.session.userId });\n\n // Fetch account number for API calls\n await this.fetchAccountNumber();\n }\n\n /**\n * Logout and clear session\n */\n async logout(): Promise<void> {\n this.session = null;\n this.accountNo = null;\n this.logger?.info('Logged out');\n }\n\n /**\n * Check if client is authenticated\n */\n isAuthenticated(): boolean {\n return this.session !== null;\n }\n\n /**\n * Get account balance\n */\n async getBalance(): Promise<Balance> {\n await this.ensureAuthenticated();\n\n if (!this.accountNo) {\n throw new AuthError('Account number not available');\n }\n\n return fetchBalance(this.httpClient, this.accountNo, this.getSessionHeaders());\n }\n\n /**\n * Get transaction history\n */\n async getTransactions(options?: TransactionOptions): Promise<Transaction[]> {\n await this.ensureAuthenticated();\n\n if (!this.accountNo) {\n throw new AuthError('Account number not available');\n }\n\n return fetchTransactions(this.httpClient, this.accountNo, this.getSessionHeaders(), options);\n }\n\n /**\n * Get account information\n * Uses fetchUserProfile internally, adds accountNo from options/session\n */\n async getAccountInfo(): Promise<AccountInfo> {\n await this.ensureAuthenticated();\n\n // Try to fetch from API first\n const profile = await fetchUserProfile(this.httpClient, this.getSessionHeaders());\n\n if (profile) {\n return {\n userId: profile.userId,\n phoneNumber: profile.phoneNumber,\n accountNo: this.accountNo ?? undefined,\n };\n }\n\n // Fallback to session data\n if (this.session) {\n return {\n userId: String(this.session.userId),\n phoneNumber: this.session.phoneNumber,\n accountNo: this.accountNo ?? undefined,\n };\n }\n\n throw new AuthError('Failed to fetch account info');\n }\n\n /**\n * Get user profile\n * Note: /user API may not be available, returns basic info from session\n */\n async getUserProfile(): Promise<UserProfile> {\n await this.ensureAuthenticated();\n\n // Try to fetch from API first\n const profile = await fetchUserProfile(this.httpClient, this.getSessionHeaders());\n\n if (profile) {\n return profile;\n }\n\n // Fallback to session data\n if (this.session) {\n return {\n userId: String(this.session.userId),\n phoneNumber: this.session.phoneNumber,\n };\n }\n\n throw new AuthError('Failed to fetch user profile');\n }\n\n /**\n * Ensure client is authenticated, login if needed\n */\n private async ensureAuthenticated(): Promise<void> {\n if (!this.session) {\n await this.login();\n }\n }\n\n /**\n * Get session headers for API requests\n */\n private getSessionHeaders() {\n if (!this.session) {\n throw new AuthError('Not authenticated');\n }\n\n return buildSessionHeaders(\n this.session.token,\n this.session.timoDeviceId,\n this.session.contextId,\n this.credentials.browserSignature\n );\n }\n\n /**\n * Fetch and store account number from /user/accountPreview\n * Falls back to provided accountNo in options if API fails\n */\n private async fetchAccountNumber(): Promise<void> {\n // Skip if already set via options\n if (this.accountNo) {\n this.logger?.debug('Using provided account number', { accountNo: this.accountNo });\n return;\n }\n\n try {\n const accountNo = await fetchSpendAccountNo(this.httpClient, this.getSessionHeaders());\n if (accountNo) {\n this.accountNo = accountNo;\n this.logger?.debug('Account number fetched from API', { accountNo });\n } else {\n this.logger?.warn('No Spend Account found. Provide accountNo in options.');\n }\n } catch {\n this.logger?.warn('Failed to fetch account number. Provide accountNo in options.');\n }\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * OS detection for browser signature\n */\nfunction getOSName(): string {\n const platform = process.platform;\n switch (platform) {\n case 'win32':\n return 'Windows';\n case 'darwin':\n return 'MacOS';\n case 'linux':\n return 'Linux';\n default:\n return 'Windows';\n }\n}\n\n/**\n * Browser name based on OS\n */\nfunction getBrowserName(): string {\n const os = getOSName();\n return os === 'MacOS' ? 'safari' : 'chrome';\n}\n\n/**\n * Generate a device fingerprint ID\n */\nexport function generateDeviceId(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * Build browser signature for current platform\n * Format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport function buildBrowserSignature(): string {\n const os = getOSName();\n const browser = getBrowserName();\n return `:WEB:${os}:297:WEB:desktop:${browser}`;\n}\n\n/**\n * Get default browser signature (Windows Chrome)\n */\nexport function getDefaultBrowserSignature(): string {\n return DEFAULT_BROWSER_SIGNATURE;\n}\n\n/**\n * Device credentials used for authentication\n */\nexport interface DeviceCredentials {\n deviceId: string;\n browserSignature: string;\n}\n\n/**\n * Generate new device credentials\n */\nexport function generateDeviceCredentials(): DeviceCredentials {\n return {\n deviceId: generateDeviceId(),\n browserSignature: buildBrowserSignature(),\n };\n}\n","/**\n * @timo-bank/core\n * Unofficial Timo Bank SDK\n */\n\nexport const VERSION = '0.1.0';\n\n// Main client\nexport { TimoClient } from './client.js';\n\n// Types\nexport * from './types/index.js';\n\n// Errors\nexport * from './errors/index.js';\n\n// Credentials\nexport * from './credentials/index.js';\n\n// Utils (selective exports)\nexport { sha512, generateUUID } from './utils/crypto.js';\nexport { API_CODES, ACCOUNT_TYPE_SPEND } from './utils/constants.js';\n\n// HTTP\nexport { HttpClient } from './http/client.js';\nexport type { RequestOptions } from './http/client.js';\nexport {\n buildDeviceReg,\n buildContextId,\n buildDeviceKey,\n buildAuthHeaders,\n buildSessionHeaders,\n} from './http/headers.js';\nexport type { AuthHeaders, SessionHeaders } from './http/headers.js';\n\n// Auth\nexport {\n generateDeviceId,\n buildBrowserSignature,\n generateDeviceCredentials,\n} from './auth/device.js';\nexport type { DeviceCredentials } from './auth/device.js';\nexport { LoginManager } from './auth/login.js';\nexport type { SessionData, OtpRequirement, LoginResult } from './auth/login.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/constants.ts","../src/errors/classes.ts","../src/http/client.ts","../src/utils/crypto.ts","../src/http/headers.ts","../src/auth/login.ts","../src/credentials/encoder.ts","../src/credentials/parser.ts","../src/api/balance.ts","../src/api/transaction.ts","../src/api/account.ts","../src/client.ts","../src/auth/device.ts","../src/index.ts"],"names":["https","createHash","randomUUID","formatDate","randomBytes"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * API configuration constants\n */\nexport const API_BASE_URL = 'https://app2.timo.vn';\nexport const API_HOSTNAME = 'app2.timo.vn';\n\n/**\n * Default browser signature format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport const DEFAULT_BROWSER_SIGNATURE = ':WEB:Windows:297:WEB:desktop:chrome';\n\n/**\n * Encryption key seed for AES encryption of device ID.\n * This is derived from Timo's public web app and is not a secret.\n * It's used to match the behavior of the official web client.\n */\nexport const ENCRYPTION_KEY_SEED = 'uuidKeyT1m0@412NTMK';\n\n/**\n * API response codes\n */\nexport const API_CODES = {\n SUCCESS: 200,\n UNAUTHORIZED: 401,\n OTP_REQUIRED: 6001,\n TWO_FACTOR_REQUIRED: 6006,\n} as const;\n\n/**\n * Default HTTP headers\n */\nexport const DEFAULT_HEADERS = {\n accept: 'application/json, text/plain, */*',\n 'content-type': 'application/json; charset=UTF-8',\n origin: 'https://my.timo.vn',\n referer: 'https://my.timo.vn/',\n 'user-agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',\n} as const;\n\n/**\n * Spend account type code\n */\nexport const ACCOUNT_TYPE_SPEND = '1025';\n","/**\n * Base error class for all Timo SDK errors\n */\nexport class TimoError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimoError';\n Object.setPrototypeOf(this, TimoError.prototype);\n }\n}\n\n/**\n * Authentication related errors\n */\nexport class AuthError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n Object.setPrototypeOf(this, AuthError.prototype);\n }\n}\n\n/**\n * Invalid or malformed credential token\n */\nexport class CredentialError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialError';\n Object.setPrototypeOf(this, CredentialError.prototype);\n }\n}\n\n/**\n * Session has expired, needs re-login\n */\nexport class SessionExpiredError extends TimoError {\n constructor(message = 'Session expired') {\n super(message);\n this.name = 'SessionExpiredError';\n Object.setPrototypeOf(this, SessionExpiredError.prototype);\n }\n}\n\n/**\n * Device has been invalidated by Timo, needs new setup\n */\nexport class DeviceInvalidatedError extends TimoError {\n constructor(message = 'Device invalidated, run setup again') {\n super(message);\n this.name = 'DeviceInvalidatedError';\n Object.setPrototypeOf(this, DeviceInvalidatedError.prototype);\n }\n}\n\n/**\n * API request failed\n */\nexport class ApiError extends TimoError {\n public readonly code: number;\n public readonly response?: unknown;\n\n constructor(message: string, code: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.code = code;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n","import * as https from 'node:https';\nimport { API_HOSTNAME, DEFAULT_HEADERS } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\nimport type { ApiResponse } from '../types/api.js';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n}\n\n/**\n * HTTP client for Timo API requests\n */\nexport class HttpClient {\n private readonly hostname: string;\n\n constructor(hostname: string = API_HOSTNAME) {\n this.hostname = hostname;\n }\n\n /**\n * Make a POST request\n */\n async post<T = unknown>(\n path: string,\n body: object,\n options: RequestOptions = {}\n ): Promise<ApiResponse<T>> {\n const data = JSON.stringify(body);\n const headers = {\n ...DEFAULT_HEADERS,\n 'content-length': Buffer.byteLength(data).toString(),\n ...options.headers,\n };\n\n return this.request<T>('POST', path, headers, data, options.timeout);\n }\n\n /**\n * Make a GET request\n */\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<ApiResponse<T>> {\n // Remove content-type for GET requests\n const { 'content-type': _contentType, ...getHeaders } = DEFAULT_HEADERS;\n const headers = {\n ...getHeaders,\n ...options.headers,\n };\n\n return this.request<T>('GET', path, headers, undefined, options.timeout);\n }\n\n private request<T>(\n method: string,\n path: string,\n headers: Record<string, string>,\n body?: string,\n timeout = 30000\n ): Promise<ApiResponse<T>> {\n return new Promise((resolve, reject) => {\n const requestOptions = {\n hostname: this.hostname,\n port: 443,\n path,\n method,\n headers,\n timeout,\n };\n\n const req = https.request(requestOptions, (res) => {\n let responseData = '';\n\n res.on('data', (chunk) => {\n responseData += chunk;\n });\n\n res.on('end', () => {\n try {\n const parsed = JSON.parse(responseData) as ApiResponse<T>;\n resolve(parsed);\n } catch {\n reject(new ApiError('Failed to parse response', res.statusCode || 500, responseData));\n }\n });\n });\n\n req.on('error', (error) => {\n reject(new ApiError(`Request failed: ${error.message}`, 0));\n });\n\n req.on('timeout', () => {\n req.destroy();\n reject(new ApiError('Request timeout', 408));\n });\n\n if (body) {\n req.write(body);\n }\n\n req.end();\n });\n }\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { ENCRYPTION_KEY_SEED } from './constants.js';\n\n/**\n * Generate SHA256 hash\n */\nexport function sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Generate SHA512 hash\n */\nexport function sha512(input: string): string {\n return createHash('sha512').update(input).digest('hex');\n}\n\n/**\n * Get derived encryption key (first 16 chars of SHA256 of seed)\n */\nexport function getEncryptionKey(): string {\n return sha256(ENCRYPTION_KEY_SEED).substring(0, 16);\n}\n\n/**\n * Generate a cryptographically secure UUID v4\n */\nexport function generateUUID(): string {\n return randomUUID();\n}\n","import { sha256, generateUUID } from '../utils/crypto.js';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * Build device registration header value\n * Format: deviceId + browserSignature\n */\nexport function buildDeviceReg(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${deviceId}${signature}`;\n}\n\n/**\n * Build context ID header value\n * Format: sha256(WEB.deviceId.297).uuid\n */\nexport function buildContextId(deviceId: string): string {\n const inputString = `WEB.${deviceId}.297`;\n const hashPart = sha256(inputString);\n const uuid = generateUUID();\n return `${hashPart}.${uuid}`;\n}\n\n/**\n * Build device key header value (used after login)\n * Format: timoDeviceId + browserSignature\n */\nexport function buildDeviceKey(\n timoDeviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${timoDeviceId}${signature}`;\n}\n\n/**\n * Build authentication headers for API requests\n */\nexport interface AuthHeaders {\n 'x-timo-devicereg': string;\n 'x-gofs-context-id': string;\n [key: string]: string;\n}\n\nexport function buildAuthHeaders(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): AuthHeaders {\n return {\n 'x-timo-devicereg': buildDeviceReg(deviceId, signature),\n 'x-gofs-context-id': buildContextId(deviceId),\n };\n}\n\n/**\n * Build headers for authenticated requests (after login)\n */\nexport interface SessionHeaders {\n token: string;\n 'x-gofs-context-id': string;\n 'x-timo-devicekey': string;\n [key: string]: string;\n}\n\nexport function buildSessionHeaders(\n token: string,\n timoDeviceId: string,\n contextId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): SessionHeaders {\n return {\n token,\n 'x-gofs-context-id': contextId,\n 'x-timo-devicekey': buildDeviceKey(timoDeviceId, signature),\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport { buildAuthHeaders, buildContextId } from '../http/headers.js';\nimport { API_CODES } from '../utils/constants.js';\nimport { AuthError, SessionExpiredError, DeviceInvalidatedError } from '../errors/classes.js';\nimport type { ApiResponse, LoginResponseData } from '../types/api.js';\n\n/**\n * Session data after successful login\n */\nexport interface SessionData {\n token: string;\n userId: string;\n phoneNumber: string;\n timoDeviceId: string;\n contextId: string;\n}\n\n/**\n * OTP requirement response\n */\nexport interface OtpRequirement {\n type: 'device' | 'twoFactor';\n refNo: string;\n token: string;\n}\n\n/**\n * Login result - either success or OTP required\n */\nexport type LoginResult =\n | { success: true; session: SessionData }\n | { success: false; otpRequired: OtpRequirement };\n\n/**\n * Login manager for handling authentication flow\n */\nexport class LoginManager {\n private readonly httpClient: HttpClient;\n private readonly deviceId: string;\n private readonly browserSignature: string;\n private readonly contextId: string;\n\n constructor(\n deviceId: string,\n browserSignature: string,\n httpClient: HttpClient = new HttpClient()\n ) {\n this.deviceId = deviceId;\n this.browserSignature = browserSignature;\n this.httpClient = httpClient;\n this.contextId = buildContextId(deviceId);\n }\n\n /**\n * Perform initial login\n * @param username - Phone number\n * @param password - Pre-hashed password (SHA512)\n */\n async login(username: string, password: string): Promise<LoginResult> {\n const authHeaders = buildAuthHeaders(this.deviceId, this.browserSignature);\n\n const response = await this.httpClient.post<LoginResponseData>(\n '/login',\n {\n username,\n password,\n lang: 'vn',\n },\n { headers: authHeaders }\n );\n\n return this.handleLoginResponse(response);\n }\n\n /**\n * Verify device OTP (code 6001)\n */\n async verifyDeviceOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n /**\n * Verify two-factor OTP (code 6006)\n */\n async verifyTwoFactorOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/afs/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n smsOTP: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n private handleLoginResponse(response: ApiResponse<LoginResponseData>): LoginResult {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return {\n success: true,\n session: this.buildSessionData(response.data),\n };\n }\n\n if (response.code === API_CODES.OTP_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'device',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n if (response.code === API_CODES.TWO_FACTOR_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'twoFactor',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n // Handle 401 - Invalid credentials\n if (response.code === API_CODES.UNAUTHORIZED) {\n throw new AuthError('Invalid phone number or password');\n }\n\n throw new AuthError(response.message || 'Login failed');\n }\n\n private handleOtpResponse(response: ApiResponse<LoginResponseData>): SessionData {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return this.buildSessionData(response.data);\n }\n\n // Check for device invalidation\n if (response.message?.includes('device') && response.message?.includes('invalid')) {\n throw new DeviceInvalidatedError();\n }\n\n // Check for session expiry\n if (response.message?.includes('session') && response.message?.includes('expired')) {\n throw new SessionExpiredError();\n }\n\n throw new AuthError(response.message || 'OTP verification failed');\n }\n\n private buildSessionData(data: LoginResponseData): SessionData {\n if (!data.timoDeviceId) {\n throw new AuthError('Missing timoDeviceId in response');\n }\n\n return {\n token: data.token,\n userId: data.userId,\n phoneNumber: data.phoneNumber,\n timoDeviceId: data.timoDeviceId,\n contextId: this.contextId,\n };\n }\n\n /**\n * Get the context ID for this login session\n */\n getContextId(): string {\n return this.contextId;\n }\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\n\nexport const TOKEN_PREFIX = 'timo_v1_';\nexport const TOKEN_VERSION = 'v1';\n\n/**\n * Encode credential data to a token string.\n * Note: Base64 encoding is NOT encryption - tokens should be treated as secrets.\n */\nexport function encodeCredentials(data: CredentialData): string {\n const json = JSON.stringify(data);\n const base64 = Buffer.from(json, 'utf-8').toString('base64');\n return `${TOKEN_PREFIX}${base64}`;\n}\n\n/**\n * Decode a credential token to credential data\n */\nexport function decodeCredentials(token: string): CredentialData {\n if (!token.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError('Invalid credential token format');\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n const data = JSON.parse(json) as CredentialData;\n\n // Validate all required fields\n if (\n !data.username ||\n !data.password ||\n !data.deviceId ||\n !data.timoDeviceId ||\n !data.browserSignature ||\n !data.createdAt\n ) {\n throw new CredentialError('Invalid credential data: missing required fields');\n }\n\n return data;\n } catch (error) {\n if (error instanceof CredentialError) {\n throw error;\n }\n throw new CredentialError('Failed to decode credential token');\n }\n}\n\n/**\n * Validate a credential token format without fully decoding\n */\nexport function isValidTokenFormat(token: string): boolean {\n if (!token.startsWith(TOKEN_PREFIX)) {\n return false;\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n JSON.parse(json);\n return true;\n } catch {\n return false;\n }\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\nimport { TOKEN_PREFIX, decodeCredentials } from './encoder.js';\n\n/**\n * Extract version from token\n */\nexport function getTokenVersion(token: string): string {\n if (token.startsWith(TOKEN_PREFIX)) {\n return 'v1';\n }\n throw new CredentialError('Unsupported credential version');\n}\n\n/**\n * Parse and validate a credential token\n */\nexport function parseCredentialToken(token: string): CredentialData {\n if (!token || typeof token !== 'string') {\n throw new CredentialError('Credential token is required');\n }\n\n const trimmed = token.trim();\n\n if (!trimmed.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError(\n `Invalid credential token format. Expected token starting with \"${TOKEN_PREFIX}\"`\n );\n }\n\n // Validate version\n getTokenVersion(trimmed);\n\n // Decode and validate\n return decodeCredentials(trimmed);\n}\n\n/**\n * Mask sensitive data in credentials for safe logging\n */\nexport function maskCredentials(data: CredentialData): Record<string, string> {\n return {\n username: maskPhone(data.username),\n password: '***',\n deviceId: maskString(data.deviceId),\n timoDeviceId: maskString(data.timoDeviceId),\n browserSignature: maskBrowserSignature(data.browserSignature),\n createdAt: data.createdAt,\n };\n}\n\nfunction maskBrowserSignature(sig: string): string {\n // Browser signature format: :WEB:OS:VERSION:WEB:device:browser\n // Keep format visible but mask specific values\n const parts = sig.split(':');\n if (parts.length >= 6) {\n return `:${parts[1]}:***:***:${parts[4]}:***:***`;\n }\n return '***';\n}\n\nfunction maskPhone(phone: string): string {\n if (phone.length <= 4) return '****';\n return phone.slice(0, 4) + '*'.repeat(phone.length - 4);\n}\n\nfunction maskString(str: string): string {\n if (str.length <= 8) return '****';\n return str.slice(0, 4) + '...' + str.slice(-4);\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { Balance } from '../types/models.js';\nimport type { TransactionListResponseData } from '../types/api.js';\nimport { ACCOUNT_TYPE_SPEND } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\n\n/**\n * Format a date as DD/MM/YYYY for Timo API\n */\nfunction formatDate(date: Date): string {\n const day = String(date.getDate()).padStart(2, '0');\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const year = date.getFullYear();\n return `${day}/${month}/${year}`;\n}\n\n/**\n * Fetch account balance via transaction list endpoint\n */\nexport async function fetchBalance(\n httpClient: HttpClient,\n accountNo: string,\n sessionHeaders: SessionHeaders\n): Promise<Balance> {\n // Use transaction list endpoint to get balance info\n const today = new Date();\n const payload = {\n format: 'group',\n index: 0,\n offset: -1,\n accountNo,\n accountType: ACCOUNT_TYPE_SPEND,\n fromDate: formatDate(today),\n toDate: formatDate(today),\n };\n\n const response = await httpClient.post<TransactionListResponseData>(\n '/user/account/transaction/list',\n payload,\n { headers: sessionHeaders }\n );\n\n if (!response.success || !response.data) {\n throw new ApiError('Failed to fetch balance', response.code);\n }\n\n return {\n accountNo,\n accountBalance: response.data.accountBalance ?? 0,\n availableAmount: response.data.availableAmount ?? 0,\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { Transaction, TransactionOptions } from '../types/models.js';\nimport type { TransactionListResponseData, TransactionItem } from '../types/api.js';\nimport { ACCOUNT_TYPE_SPEND } from '../utils/constants.js';\n\n/**\n * Format a date as DD/MM/YYYY for Timo API\n */\nfunction formatDate(date: Date): string {\n const day = String(date.getDate()).padStart(2, '0');\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const year = date.getFullYear();\n return `${day}/${month}/${year}`;\n}\n\n/**\n * Get default date range (last 30 days)\n */\nfunction getDefaultDateRange(): { fromDate: string; toDate: string } {\n const today = new Date();\n const thirtyDaysAgo = new Date(today);\n thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);\n\n return {\n fromDate: formatDate(thirtyDaysAgo),\n toDate: formatDate(today),\n };\n}\n\n/**\n * Map API transaction item to Transaction model\n */\nfunction mapTransaction(item: TransactionItem): Transaction {\n return {\n refNo: item.refNo,\n txnAmount: item.txnAmount,\n txnDesc: item.txnDesc,\n txnTime: item.txnTime,\n txnType: item.txnType,\n transactionTime: item.transactionTime,\n txnBankName: item.txnBankName,\n txnBankAccount: item.txnBankAccount,\n };\n}\n\n/**\n * Fetch transaction history\n */\nexport async function fetchTransactions(\n httpClient: HttpClient,\n accountNo: string,\n sessionHeaders: SessionHeaders,\n options?: TransactionOptions\n): Promise<Transaction[]> {\n const dateRange = getDefaultDateRange();\n\n const payload = {\n format: 'group',\n index: 0,\n offset: -1,\n accountNo,\n accountType: ACCOUNT_TYPE_SPEND,\n fromDate: options?.fromDate ?? dateRange.fromDate,\n toDate: options?.toDate ?? dateRange.toDate,\n };\n\n const response = await httpClient.post<TransactionListResponseData>(\n '/user/account/transaction/list',\n payload,\n { headers: sessionHeaders }\n );\n\n if (!response.success || !response.data) {\n return [];\n }\n\n // Flatten grouped transactions\n const allTransactions: Transaction[] = [];\n for (const group of response.data.items ?? []) {\n for (const item of group.item ?? []) {\n allTransactions.push(mapTransaction(item));\n }\n }\n\n // Apply limit if specified\n if (options?.limit && options.limit > 0) {\n return allTransactions.slice(0, options.limit);\n }\n\n return allTransactions;\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { UserProfile, Account, AccountType } from '../types/models.js';\n\n/** Account type mapping from API codes */\nconst ACCOUNT_TYPE_MAP: Record<string, AccountType> = {\n '1025': 'spend',\n '1026': 'goal',\n '1027': 'term',\n '1028': 'money_market',\n '9999': 'credit',\n '1910': 'split_bill',\n '6511': 'insurance',\n};\n\ninterface AccountPreviewItem {\n no: string;\n accountType: string;\n name: string;\n balance?: number;\n accountBalance?: number;\n availableAmount?: number;\n}\n\ninterface AccountPreviewResponse {\n accounts: AccountPreviewItem[];\n}\n\ninterface ProfileResponseData {\n userId: number;\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n legalId?: string;\n cityName?: string;\n address?: string;\n birthday?: string;\n gender?: string;\n}\n\n/**\n * Fetch user profile from /user/getProfile\n * Note: accountNo is not available from this API - must be provided via options\n */\nexport async function fetchUserProfile(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<UserProfile | null> {\n const response = await httpClient.get<ProfileResponseData>('/user/getProfile', {\n headers: sessionHeaders,\n });\n\n if (!response.success || !response.data) {\n return null;\n }\n\n const data = response.data;\n const fullName = `${data.firstName} ${data.lastName}`.trim();\n\n return {\n userId: String(data.userId),\n phoneNumber: data.phone,\n fullName: fullName || undefined,\n email: data.email,\n };\n}\n\n/**\n * Fetch all accounts from /user/accountPreview\n * Returns list of all user accounts (spend, goal, term, etc.)\n */\nexport async function fetchAccounts(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<Account[]> {\n const response = await httpClient.get<AccountPreviewResponse>('/user/accountPreview', {\n headers: sessionHeaders,\n });\n\n if (!response.success || !response.data?.accounts) {\n return [];\n }\n\n return response.data.accounts\n .filter((item) => item.no) // Only include accounts with a number\n .map((item) => ({\n no: item.no,\n accountType: item.accountType,\n type: ACCOUNT_TYPE_MAP[item.accountType] ?? 'spend',\n name: item.name,\n balance: item.balance,\n accountBalance: item.accountBalance,\n availableAmount: item.availableAmount,\n }));\n}\n\n/**\n * Fetch spend account number from /user/accountPreview\n * Returns the main Spend Account number (accountType: 1025)\n */\nexport async function fetchSpendAccountNo(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<string | null> {\n const accounts = await fetchAccounts(httpClient, sessionHeaders);\n const spendAccount = accounts.find((a) => a.type === 'spend');\n return spendAccount?.no ?? null;\n}\n","import { HttpClient } from './http/client.js';\nimport { buildSessionHeaders } from './http/headers.js';\nimport { LoginManager } from './auth/login.js';\nimport type { SessionData } from './auth/login.js';\nimport { parseCredentialToken } from './credentials/parser.js';\nimport { fetchBalance } from './api/balance.js';\nimport { fetchTransactions } from './api/transaction.js';\nimport { fetchUserProfile, fetchSpendAccountNo } from './api/account.js';\nimport type { TimoClientOptions, CredentialData, Logger } from './types/config.js';\nimport type { Balance, Transaction, TransactionOptions, AccountInfo, UserProfile } from './types/models.js';\nimport { AuthError } from './errors/classes.js';\n\n/**\n * Main Timo Bank client\n */\nexport class TimoClient {\n private readonly credentials: CredentialData;\n private readonly httpClient: HttpClient;\n private readonly logger?: Logger;\n private session: SessionData | null = null;\n private accountNo: string | null = null;\n\n constructor(options: TimoClientOptions) {\n this.credentials = parseCredentialToken(options.credentials);\n this.httpClient = new HttpClient();\n this.logger = options.logger;\n this.accountNo = options.accountNo ?? null;\n }\n\n /**\n * Login to Timo Bank\n * Uses saved device credentials for automatic login without OTP\n */\n async login(): Promise<void> {\n const loginManager = new LoginManager(\n this.credentials.deviceId,\n this.credentials.browserSignature,\n this.httpClient\n );\n\n // Login using saved credentials (should not require OTP with valid timoDeviceId)\n const result = await loginManager.login(this.credentials.username, this.credentials.password);\n\n if (!result.success) {\n throw new AuthError(\n `Device requires re-authorization. OTP type: ${result.otpRequired.type}. ` +\n 'Run \"npx @timo-bank/core setup\" to re-authorize this device.'\n );\n }\n\n this.session = result.session;\n this.logger?.info('Login successful', { userId: this.session.userId });\n\n // Fetch account number for API calls\n await this.fetchAccountNumber();\n }\n\n /**\n * Logout and clear session\n */\n async logout(): Promise<void> {\n this.session = null;\n this.accountNo = null;\n this.logger?.info('Logged out');\n }\n\n /**\n * Check if client is authenticated\n */\n isAuthenticated(): boolean {\n return this.session !== null;\n }\n\n /**\n * Get account balance\n */\n async getBalance(): Promise<Balance> {\n await this.ensureAuthenticated();\n\n if (!this.accountNo) {\n throw new AuthError('Account number not available');\n }\n\n return fetchBalance(this.httpClient, this.accountNo, this.getSessionHeaders());\n }\n\n /**\n * Get transaction history\n */\n async getTransactions(options?: TransactionOptions): Promise<Transaction[]> {\n await this.ensureAuthenticated();\n\n if (!this.accountNo) {\n throw new AuthError('Account number not available');\n }\n\n return fetchTransactions(this.httpClient, this.accountNo, this.getSessionHeaders(), options);\n }\n\n /**\n * Get account information\n * Uses fetchUserProfile internally, adds accountNo from options/session\n */\n async getAccountInfo(): Promise<AccountInfo> {\n await this.ensureAuthenticated();\n\n // Try to fetch from API first\n const profile = await fetchUserProfile(this.httpClient, this.getSessionHeaders());\n\n if (profile) {\n return {\n userId: profile.userId,\n phoneNumber: profile.phoneNumber,\n accountNo: this.accountNo ?? undefined,\n };\n }\n\n // Fallback to session data\n if (this.session) {\n return {\n userId: String(this.session.userId),\n phoneNumber: this.session.phoneNumber,\n accountNo: this.accountNo ?? undefined,\n };\n }\n\n throw new AuthError('Failed to fetch account info');\n }\n\n /**\n * Get user profile\n * Note: /user API may not be available, returns basic info from session\n */\n async getUserProfile(): Promise<UserProfile> {\n await this.ensureAuthenticated();\n\n // Try to fetch from API first\n const profile = await fetchUserProfile(this.httpClient, this.getSessionHeaders());\n\n if (profile) {\n return profile;\n }\n\n // Fallback to session data\n if (this.session) {\n return {\n userId: String(this.session.userId),\n phoneNumber: this.session.phoneNumber,\n };\n }\n\n throw new AuthError('Failed to fetch user profile');\n }\n\n /**\n * Ensure client is authenticated, login if needed\n */\n private async ensureAuthenticated(): Promise<void> {\n if (!this.session) {\n await this.login();\n }\n }\n\n /**\n * Get session headers for API requests\n */\n private getSessionHeaders() {\n if (!this.session) {\n throw new AuthError('Not authenticated');\n }\n\n return buildSessionHeaders(\n this.session.token,\n this.session.timoDeviceId,\n this.session.contextId,\n this.credentials.browserSignature\n );\n }\n\n /**\n * Fetch and store account number from /user/accountPreview\n * Falls back to provided accountNo in options if API fails\n */\n private async fetchAccountNumber(): Promise<void> {\n // Skip if already set via options\n if (this.accountNo) {\n this.logger?.debug('Using provided account number', { accountNo: this.accountNo });\n return;\n }\n\n try {\n const accountNo = await fetchSpendAccountNo(this.httpClient, this.getSessionHeaders());\n if (accountNo) {\n this.accountNo = accountNo;\n this.logger?.debug('Account number fetched from API', { accountNo });\n } else {\n this.logger?.warn('No Spend Account found. Provide accountNo in options.');\n }\n } catch {\n this.logger?.warn('Failed to fetch account number. Provide accountNo in options.');\n }\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * OS detection for browser signature\n */\nfunction getOSName(): string {\n const platform = process.platform;\n switch (platform) {\n case 'win32':\n return 'Windows';\n case 'darwin':\n return 'MacOS';\n case 'linux':\n return 'Linux';\n default:\n return 'Windows';\n }\n}\n\n/**\n * Browser name based on OS\n */\nfunction getBrowserName(): string {\n const os = getOSName();\n return os === 'MacOS' ? 'safari' : 'chrome';\n}\n\n/**\n * Generate a device fingerprint ID\n */\nexport function generateDeviceId(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * Build browser signature for current platform\n * Format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport function buildBrowserSignature(): string {\n const os = getOSName();\n const browser = getBrowserName();\n return `:WEB:${os}:297:WEB:desktop:${browser}`;\n}\n\n/**\n * Get default browser signature (Windows Chrome)\n */\nexport function getDefaultBrowserSignature(): string {\n return DEFAULT_BROWSER_SIGNATURE;\n}\n\n/**\n * Device credentials used for authentication\n */\nexport interface DeviceCredentials {\n deviceId: string;\n browserSignature: string;\n}\n\n/**\n * Generate new device credentials\n */\nexport function generateDeviceCredentials(): DeviceCredentials {\n return {\n deviceId: generateDeviceId(),\n browserSignature: buildBrowserSignature(),\n };\n}\n","/**\n * @timo-bank/core\n * Unofficial Timo Bank SDK\n */\n\nexport const VERSION = '0.1.0';\n\n// Main client\nexport { TimoClient } from './client.js';\n\n// Types\nexport * from './types/index.js';\n\n// Errors\nexport * from './errors/index.js';\n\n// Credentials\nexport * from './credentials/index.js';\n\n// Utils (selective exports)\nexport { sha512, generateUUID } from './utils/crypto.js';\nexport { API_CODES, ACCOUNT_TYPE_SPEND } from './utils/constants.js';\n\n// HTTP\nexport { HttpClient } from './http/client.js';\nexport type { RequestOptions } from './http/client.js';\nexport {\n buildDeviceReg,\n buildContextId,\n buildDeviceKey,\n buildAuthHeaders,\n buildSessionHeaders,\n} from './http/headers.js';\nexport type { AuthHeaders, SessionHeaders } from './http/headers.js';\n\n// Auth\nexport {\n generateDeviceId,\n buildBrowserSignature,\n generateDeviceCredentials,\n} from './auth/device.js';\nexport type { DeviceCredentials } from './auth/device.js';\nexport { LoginManager } from './auth/login.js';\nexport type { SessionData, OtpRequirement, LoginResult } from './auth/login.js';\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -91,7 +91,7 @@ var HttpClient = class {
|
|
|
91
91
|
* Make a GET request
|
|
92
92
|
*/
|
|
93
93
|
async get(path, options = {}) {
|
|
94
|
-
const { "content-type":
|
|
94
|
+
const { "content-type": _contentType, ...getHeaders } = DEFAULT_HEADERS;
|
|
95
95
|
const headers = {
|
|
96
96
|
...getHeaders,
|
|
97
97
|
...options.headers
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/constants.ts","../src/errors/classes.ts","../src/http/client.ts","../src/utils/crypto.ts","../src/http/headers.ts","../src/auth/login.ts","../src/credentials/encoder.ts","../src/credentials/parser.ts","../src/api/balance.ts","../src/api/transaction.ts","../src/api/account.ts","../src/client.ts","../src/auth/device.ts","../src/index.ts"],"names":["formatDate"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * API configuration constants\n */\nexport const API_BASE_URL = 'https://app2.timo.vn';\nexport const API_HOSTNAME = 'app2.timo.vn';\n\n/**\n * Default browser signature format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport const DEFAULT_BROWSER_SIGNATURE = ':WEB:Windows:297:WEB:desktop:chrome';\n\n/**\n * Encryption key seed for AES encryption of device ID.\n * This is derived from Timo's public web app and is not a secret.\n * It's used to match the behavior of the official web client.\n */\nexport const ENCRYPTION_KEY_SEED = 'uuidKeyT1m0@412NTMK';\n\n/**\n * API response codes\n */\nexport const API_CODES = {\n SUCCESS: 200,\n UNAUTHORIZED: 401,\n OTP_REQUIRED: 6001,\n TWO_FACTOR_REQUIRED: 6006,\n} as const;\n\n/**\n * Default HTTP headers\n */\nexport const DEFAULT_HEADERS = {\n accept: 'application/json, text/plain, */*',\n 'content-type': 'application/json; charset=UTF-8',\n origin: 'https://my.timo.vn',\n referer: 'https://my.timo.vn/',\n 'user-agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',\n} as const;\n\n/**\n * Spend account type code\n */\nexport const ACCOUNT_TYPE_SPEND = '1025';\n","/**\n * Base error class for all Timo SDK errors\n */\nexport class TimoError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimoError';\n Object.setPrototypeOf(this, TimoError.prototype);\n }\n}\n\n/**\n * Authentication related errors\n */\nexport class AuthError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n Object.setPrototypeOf(this, AuthError.prototype);\n }\n}\n\n/**\n * Invalid or malformed credential token\n */\nexport class CredentialError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialError';\n Object.setPrototypeOf(this, CredentialError.prototype);\n }\n}\n\n/**\n * Session has expired, needs re-login\n */\nexport class SessionExpiredError extends TimoError {\n constructor(message = 'Session expired') {\n super(message);\n this.name = 'SessionExpiredError';\n Object.setPrototypeOf(this, SessionExpiredError.prototype);\n }\n}\n\n/**\n * Device has been invalidated by Timo, needs new setup\n */\nexport class DeviceInvalidatedError extends TimoError {\n constructor(message = 'Device invalidated, run setup again') {\n super(message);\n this.name = 'DeviceInvalidatedError';\n Object.setPrototypeOf(this, DeviceInvalidatedError.prototype);\n }\n}\n\n/**\n * API request failed\n */\nexport class ApiError extends TimoError {\n public readonly code: number;\n public readonly response?: unknown;\n\n constructor(message: string, code: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.code = code;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n","import * as https from 'node:https';\nimport { API_HOSTNAME, DEFAULT_HEADERS } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\nimport type { ApiResponse } from '../types/api.js';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n}\n\n/**\n * HTTP client for Timo API requests\n */\nexport class HttpClient {\n private readonly hostname: string;\n\n constructor(hostname: string = API_HOSTNAME) {\n this.hostname = hostname;\n }\n\n /**\n * Make a POST request\n */\n async post<T = unknown>(\n path: string,\n body: object,\n options: RequestOptions = {}\n ): Promise<ApiResponse<T>> {\n const data = JSON.stringify(body);\n const headers = {\n ...DEFAULT_HEADERS,\n 'content-length': Buffer.byteLength(data).toString(),\n ...options.headers,\n };\n\n return this.request<T>('POST', path, headers, data, options.timeout);\n }\n\n /**\n * Make a GET request\n */\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<ApiResponse<T>> {\n // Remove content-type for GET requests\n const { 'content-type': _, ...getHeaders } = DEFAULT_HEADERS;\n const headers = {\n ...getHeaders,\n ...options.headers,\n };\n\n return this.request<T>('GET', path, headers, undefined, options.timeout);\n }\n\n private request<T>(\n method: string,\n path: string,\n headers: Record<string, string>,\n body?: string,\n timeout = 30000\n ): Promise<ApiResponse<T>> {\n return new Promise((resolve, reject) => {\n const requestOptions = {\n hostname: this.hostname,\n port: 443,\n path,\n method,\n headers,\n timeout,\n };\n\n const req = https.request(requestOptions, (res) => {\n let responseData = '';\n\n res.on('data', (chunk) => {\n responseData += chunk;\n });\n\n res.on('end', () => {\n try {\n const parsed = JSON.parse(responseData) as ApiResponse<T>;\n resolve(parsed);\n } catch {\n reject(new ApiError('Failed to parse response', res.statusCode || 500, responseData));\n }\n });\n });\n\n req.on('error', (error) => {\n reject(new ApiError(`Request failed: ${error.message}`, 0));\n });\n\n req.on('timeout', () => {\n req.destroy();\n reject(new ApiError('Request timeout', 408));\n });\n\n if (body) {\n req.write(body);\n }\n\n req.end();\n });\n }\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { ENCRYPTION_KEY_SEED } from './constants.js';\n\n/**\n * Generate SHA256 hash\n */\nexport function sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Generate SHA512 hash\n */\nexport function sha512(input: string): string {\n return createHash('sha512').update(input).digest('hex');\n}\n\n/**\n * Get derived encryption key (first 16 chars of SHA256 of seed)\n */\nexport function getEncryptionKey(): string {\n return sha256(ENCRYPTION_KEY_SEED).substring(0, 16);\n}\n\n/**\n * Generate a cryptographically secure UUID v4\n */\nexport function generateUUID(): string {\n return randomUUID();\n}\n","import { sha256, generateUUID } from '../utils/crypto.js';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * Build device registration header value\n * Format: deviceId + browserSignature\n */\nexport function buildDeviceReg(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${deviceId}${signature}`;\n}\n\n/**\n * Build context ID header value\n * Format: sha256(WEB.deviceId.297).uuid\n */\nexport function buildContextId(deviceId: string): string {\n const inputString = `WEB.${deviceId}.297`;\n const hashPart = sha256(inputString);\n const uuid = generateUUID();\n return `${hashPart}.${uuid}`;\n}\n\n/**\n * Build device key header value (used after login)\n * Format: timoDeviceId + browserSignature\n */\nexport function buildDeviceKey(\n timoDeviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${timoDeviceId}${signature}`;\n}\n\n/**\n * Build authentication headers for API requests\n */\nexport interface AuthHeaders {\n 'x-timo-devicereg': string;\n 'x-gofs-context-id': string;\n [key: string]: string;\n}\n\nexport function buildAuthHeaders(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): AuthHeaders {\n return {\n 'x-timo-devicereg': buildDeviceReg(deviceId, signature),\n 'x-gofs-context-id': buildContextId(deviceId),\n };\n}\n\n/**\n * Build headers for authenticated requests (after login)\n */\nexport interface SessionHeaders {\n token: string;\n 'x-gofs-context-id': string;\n 'x-timo-devicekey': string;\n [key: string]: string;\n}\n\nexport function buildSessionHeaders(\n token: string,\n timoDeviceId: string,\n contextId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): SessionHeaders {\n return {\n token,\n 'x-gofs-context-id': contextId,\n 'x-timo-devicekey': buildDeviceKey(timoDeviceId, signature),\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport { buildAuthHeaders, buildContextId } from '../http/headers.js';\nimport { API_CODES } from '../utils/constants.js';\nimport { AuthError, SessionExpiredError, DeviceInvalidatedError } from '../errors/classes.js';\nimport type { ApiResponse, LoginResponseData } from '../types/api.js';\n\n/**\n * Session data after successful login\n */\nexport interface SessionData {\n token: string;\n userId: string;\n phoneNumber: string;\n timoDeviceId: string;\n contextId: string;\n}\n\n/**\n * OTP requirement response\n */\nexport interface OtpRequirement {\n type: 'device' | 'twoFactor';\n refNo: string;\n token: string;\n}\n\n/**\n * Login result - either success or OTP required\n */\nexport type LoginResult =\n | { success: true; session: SessionData }\n | { success: false; otpRequired: OtpRequirement };\n\n/**\n * Login manager for handling authentication flow\n */\nexport class LoginManager {\n private readonly httpClient: HttpClient;\n private readonly deviceId: string;\n private readonly browserSignature: string;\n private readonly contextId: string;\n\n constructor(\n deviceId: string,\n browserSignature: string,\n httpClient: HttpClient = new HttpClient()\n ) {\n this.deviceId = deviceId;\n this.browserSignature = browserSignature;\n this.httpClient = httpClient;\n this.contextId = buildContextId(deviceId);\n }\n\n /**\n * Perform initial login\n * @param username - Phone number\n * @param password - Pre-hashed password (SHA512)\n */\n async login(username: string, password: string): Promise<LoginResult> {\n const authHeaders = buildAuthHeaders(this.deviceId, this.browserSignature);\n\n const response = await this.httpClient.post<LoginResponseData>(\n '/login',\n {\n username,\n password,\n lang: 'vn',\n },\n { headers: authHeaders }\n );\n\n return this.handleLoginResponse(response);\n }\n\n /**\n * Verify device OTP (code 6001)\n */\n async verifyDeviceOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n /**\n * Verify two-factor OTP (code 6006)\n */\n async verifyTwoFactorOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/afs/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n smsOTP: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n private handleLoginResponse(response: ApiResponse<LoginResponseData>): LoginResult {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return {\n success: true,\n session: this.buildSessionData(response.data),\n };\n }\n\n if (response.code === API_CODES.OTP_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'device',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n if (response.code === API_CODES.TWO_FACTOR_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'twoFactor',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n // Handle 401 - Invalid credentials\n if (response.code === API_CODES.UNAUTHORIZED) {\n throw new AuthError('Invalid phone number or password');\n }\n\n throw new AuthError(response.message || 'Login failed');\n }\n\n private handleOtpResponse(response: ApiResponse<LoginResponseData>): SessionData {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return this.buildSessionData(response.data);\n }\n\n // Check for device invalidation\n if (response.message?.includes('device') && response.message?.includes('invalid')) {\n throw new DeviceInvalidatedError();\n }\n\n // Check for session expiry\n if (response.message?.includes('session') && response.message?.includes('expired')) {\n throw new SessionExpiredError();\n }\n\n throw new AuthError(response.message || 'OTP verification failed');\n }\n\n private buildSessionData(data: LoginResponseData): SessionData {\n if (!data.timoDeviceId) {\n throw new AuthError('Missing timoDeviceId in response');\n }\n\n return {\n token: data.token,\n userId: data.userId,\n phoneNumber: data.phoneNumber,\n timoDeviceId: data.timoDeviceId,\n contextId: this.contextId,\n };\n }\n\n /**\n * Get the context ID for this login session\n */\n getContextId(): string {\n return this.contextId;\n }\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\n\nexport const TOKEN_PREFIX = 'timo_v1_';\nexport const TOKEN_VERSION = 'v1';\n\n/**\n * Encode credential data to a token string.\n * Note: Base64 encoding is NOT encryption - tokens should be treated as secrets.\n */\nexport function encodeCredentials(data: CredentialData): string {\n const json = JSON.stringify(data);\n const base64 = Buffer.from(json, 'utf-8').toString('base64');\n return `${TOKEN_PREFIX}${base64}`;\n}\n\n/**\n * Decode a credential token to credential data\n */\nexport function decodeCredentials(token: string): CredentialData {\n if (!token.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError('Invalid credential token format');\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n const data = JSON.parse(json) as CredentialData;\n\n // Validate all required fields\n if (\n !data.username ||\n !data.password ||\n !data.deviceId ||\n !data.timoDeviceId ||\n !data.browserSignature ||\n !data.createdAt\n ) {\n throw new CredentialError('Invalid credential data: missing required fields');\n }\n\n return data;\n } catch (error) {\n if (error instanceof CredentialError) {\n throw error;\n }\n throw new CredentialError('Failed to decode credential token');\n }\n}\n\n/**\n * Validate a credential token format without fully decoding\n */\nexport function isValidTokenFormat(token: string): boolean {\n if (!token.startsWith(TOKEN_PREFIX)) {\n return false;\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n JSON.parse(json);\n return true;\n } catch {\n return false;\n }\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\nimport { TOKEN_PREFIX, decodeCredentials } from './encoder.js';\n\n/**\n * Extract version from token\n */\nexport function getTokenVersion(token: string): string {\n if (token.startsWith(TOKEN_PREFIX)) {\n return 'v1';\n }\n throw new CredentialError('Unsupported credential version');\n}\n\n/**\n * Parse and validate a credential token\n */\nexport function parseCredentialToken(token: string): CredentialData {\n if (!token || typeof token !== 'string') {\n throw new CredentialError('Credential token is required');\n }\n\n const trimmed = token.trim();\n\n if (!trimmed.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError(\n `Invalid credential token format. Expected token starting with \"${TOKEN_PREFIX}\"`\n );\n }\n\n // Validate version\n getTokenVersion(trimmed);\n\n // Decode and validate\n return decodeCredentials(trimmed);\n}\n\n/**\n * Mask sensitive data in credentials for safe logging\n */\nexport function maskCredentials(data: CredentialData): Record<string, string> {\n return {\n username: maskPhone(data.username),\n password: '***',\n deviceId: maskString(data.deviceId),\n timoDeviceId: maskString(data.timoDeviceId),\n browserSignature: maskBrowserSignature(data.browserSignature),\n createdAt: data.createdAt,\n };\n}\n\nfunction maskBrowserSignature(sig: string): string {\n // Browser signature format: :WEB:OS:VERSION:WEB:device:browser\n // Keep format visible but mask specific values\n const parts = sig.split(':');\n if (parts.length >= 6) {\n return `:${parts[1]}:***:***:${parts[4]}:***:***`;\n }\n return '***';\n}\n\nfunction maskPhone(phone: string): string {\n if (phone.length <= 4) return '****';\n return phone.slice(0, 4) + '*'.repeat(phone.length - 4);\n}\n\nfunction maskString(str: string): string {\n if (str.length <= 8) return '****';\n return str.slice(0, 4) + '...' + str.slice(-4);\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { Balance } from '../types/models.js';\nimport type { TransactionListResponseData } from '../types/api.js';\nimport { ACCOUNT_TYPE_SPEND } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\n\n/**\n * Format a date as DD/MM/YYYY for Timo API\n */\nfunction formatDate(date: Date): string {\n const day = String(date.getDate()).padStart(2, '0');\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const year = date.getFullYear();\n return `${day}/${month}/${year}`;\n}\n\n/**\n * Fetch account balance via transaction list endpoint\n */\nexport async function fetchBalance(\n httpClient: HttpClient,\n accountNo: string,\n sessionHeaders: SessionHeaders\n): Promise<Balance> {\n // Use transaction list endpoint to get balance info\n const today = new Date();\n const payload = {\n format: 'group',\n index: 0,\n offset: -1,\n accountNo,\n accountType: ACCOUNT_TYPE_SPEND,\n fromDate: formatDate(today),\n toDate: formatDate(today),\n };\n\n const response = await httpClient.post<TransactionListResponseData>(\n '/user/account/transaction/list',\n payload,\n { headers: sessionHeaders }\n );\n\n if (!response.success || !response.data) {\n throw new ApiError('Failed to fetch balance', response.code);\n }\n\n return {\n accountNo,\n accountBalance: response.data.accountBalance ?? 0,\n availableAmount: response.data.availableAmount ?? 0,\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { Transaction, TransactionOptions } from '../types/models.js';\nimport type { TransactionListResponseData, TransactionItem } from '../types/api.js';\nimport { ACCOUNT_TYPE_SPEND } from '../utils/constants.js';\n\n/**\n * Format a date as DD/MM/YYYY for Timo API\n */\nfunction formatDate(date: Date): string {\n const day = String(date.getDate()).padStart(2, '0');\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const year = date.getFullYear();\n return `${day}/${month}/${year}`;\n}\n\n/**\n * Get default date range (last 30 days)\n */\nfunction getDefaultDateRange(): { fromDate: string; toDate: string } {\n const today = new Date();\n const thirtyDaysAgo = new Date(today);\n thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);\n\n return {\n fromDate: formatDate(thirtyDaysAgo),\n toDate: formatDate(today),\n };\n}\n\n/**\n * Map API transaction item to Transaction model\n */\nfunction mapTransaction(item: TransactionItem): Transaction {\n return {\n refNo: item.refNo,\n txnAmount: item.txnAmount,\n txnDesc: item.txnDesc,\n txnTime: item.txnTime,\n txnType: item.txnType,\n transactionTime: item.transactionTime,\n txnBankName: item.txnBankName,\n txnBankAccount: item.txnBankAccount,\n };\n}\n\n/**\n * Fetch transaction history\n */\nexport async function fetchTransactions(\n httpClient: HttpClient,\n accountNo: string,\n sessionHeaders: SessionHeaders,\n options?: TransactionOptions\n): Promise<Transaction[]> {\n const dateRange = getDefaultDateRange();\n\n const payload = {\n format: 'group',\n index: 0,\n offset: -1,\n accountNo,\n accountType: ACCOUNT_TYPE_SPEND,\n fromDate: options?.fromDate ?? dateRange.fromDate,\n toDate: options?.toDate ?? dateRange.toDate,\n };\n\n const response = await httpClient.post<TransactionListResponseData>(\n '/user/account/transaction/list',\n payload,\n { headers: sessionHeaders }\n );\n\n if (!response.success || !response.data) {\n return [];\n }\n\n // Flatten grouped transactions\n const allTransactions: Transaction[] = [];\n for (const group of response.data.items ?? []) {\n for (const item of group.item ?? []) {\n allTransactions.push(mapTransaction(item));\n }\n }\n\n // Apply limit if specified\n if (options?.limit && options.limit > 0) {\n return allTransactions.slice(0, options.limit);\n }\n\n return allTransactions;\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { UserProfile, Account, AccountType } from '../types/models.js';\n\n/** Account type mapping from API codes */\nconst ACCOUNT_TYPE_MAP: Record<string, AccountType> = {\n '1025': 'spend',\n '1026': 'goal',\n '1027': 'term',\n '1028': 'money_market',\n '9999': 'credit',\n '1910': 'split_bill',\n '6511': 'insurance',\n};\n\ninterface AccountPreviewItem {\n no: string;\n accountType: string;\n name: string;\n balance?: number;\n accountBalance?: number;\n availableAmount?: number;\n}\n\ninterface AccountPreviewResponse {\n accounts: AccountPreviewItem[];\n}\n\ninterface ProfileResponseData {\n userId: number;\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n legalId?: string;\n cityName?: string;\n address?: string;\n birthday?: string;\n gender?: string;\n}\n\n/**\n * Fetch user profile from /user/getProfile\n * Note: accountNo is not available from this API - must be provided via options\n */\nexport async function fetchUserProfile(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<UserProfile | null> {\n const response = await httpClient.get<ProfileResponseData>('/user/getProfile', {\n headers: sessionHeaders,\n });\n\n if (!response.success || !response.data) {\n return null;\n }\n\n const data = response.data;\n const fullName = `${data.firstName} ${data.lastName}`.trim();\n\n return {\n userId: String(data.userId),\n phoneNumber: data.phone,\n fullName: fullName || undefined,\n email: data.email,\n };\n}\n\n/**\n * Fetch all accounts from /user/accountPreview\n * Returns list of all user accounts (spend, goal, term, etc.)\n */\nexport async function fetchAccounts(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<Account[]> {\n const response = await httpClient.get<AccountPreviewResponse>('/user/accountPreview', {\n headers: sessionHeaders,\n });\n\n if (!response.success || !response.data?.accounts) {\n return [];\n }\n\n return response.data.accounts\n .filter((item) => item.no) // Only include accounts with a number\n .map((item) => ({\n no: item.no,\n accountType: item.accountType,\n type: ACCOUNT_TYPE_MAP[item.accountType] ?? 'spend',\n name: item.name,\n balance: item.balance,\n accountBalance: item.accountBalance,\n availableAmount: item.availableAmount,\n }));\n}\n\n/**\n * Fetch spend account number from /user/accountPreview\n * Returns the main Spend Account number (accountType: 1025)\n */\nexport async function fetchSpendAccountNo(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<string | null> {\n const accounts = await fetchAccounts(httpClient, sessionHeaders);\n const spendAccount = accounts.find((a) => a.type === 'spend');\n return spendAccount?.no ?? null;\n}\n","import { HttpClient } from './http/client.js';\nimport { buildSessionHeaders } from './http/headers.js';\nimport { LoginManager } from './auth/login.js';\nimport type { SessionData } from './auth/login.js';\nimport { parseCredentialToken } from './credentials/parser.js';\nimport { fetchBalance } from './api/balance.js';\nimport { fetchTransactions } from './api/transaction.js';\nimport { fetchUserProfile, fetchSpendAccountNo } from './api/account.js';\nimport type { TimoClientOptions, CredentialData, Logger } from './types/config.js';\nimport type { Balance, Transaction, TransactionOptions, AccountInfo, UserProfile } from './types/models.js';\nimport { AuthError } from './errors/classes.js';\n\n/**\n * Main Timo Bank client\n */\nexport class TimoClient {\n private readonly credentials: CredentialData;\n private readonly httpClient: HttpClient;\n private readonly logger?: Logger;\n private session: SessionData | null = null;\n private accountNo: string | null = null;\n\n constructor(options: TimoClientOptions) {\n this.credentials = parseCredentialToken(options.credentials);\n this.httpClient = new HttpClient();\n this.logger = options.logger;\n this.accountNo = options.accountNo ?? null;\n }\n\n /**\n * Login to Timo Bank\n * Uses saved device credentials for automatic login without OTP\n */\n async login(): Promise<void> {\n const loginManager = new LoginManager(\n this.credentials.deviceId,\n this.credentials.browserSignature,\n this.httpClient\n );\n\n // Login using saved credentials (should not require OTP with valid timoDeviceId)\n const result = await loginManager.login(this.credentials.username, this.credentials.password);\n\n if (!result.success) {\n throw new AuthError(\n `Device requires re-authorization. OTP type: ${result.otpRequired.type}. ` +\n 'Run \"npx @timo-bank/core setup\" to re-authorize this device.'\n );\n }\n\n this.session = result.session;\n this.logger?.info('Login successful', { userId: this.session.userId });\n\n // Fetch account number for API calls\n await this.fetchAccountNumber();\n }\n\n /**\n * Logout and clear session\n */\n async logout(): Promise<void> {\n this.session = null;\n this.accountNo = null;\n this.logger?.info('Logged out');\n }\n\n /**\n * Check if client is authenticated\n */\n isAuthenticated(): boolean {\n return this.session !== null;\n }\n\n /**\n * Get account balance\n */\n async getBalance(): Promise<Balance> {\n await this.ensureAuthenticated();\n\n if (!this.accountNo) {\n throw new AuthError('Account number not available');\n }\n\n return fetchBalance(this.httpClient, this.accountNo, this.getSessionHeaders());\n }\n\n /**\n * Get transaction history\n */\n async getTransactions(options?: TransactionOptions): Promise<Transaction[]> {\n await this.ensureAuthenticated();\n\n if (!this.accountNo) {\n throw new AuthError('Account number not available');\n }\n\n return fetchTransactions(this.httpClient, this.accountNo, this.getSessionHeaders(), options);\n }\n\n /**\n * Get account information\n * Uses fetchUserProfile internally, adds accountNo from options/session\n */\n async getAccountInfo(): Promise<AccountInfo> {\n await this.ensureAuthenticated();\n\n // Try to fetch from API first\n const profile = await fetchUserProfile(this.httpClient, this.getSessionHeaders());\n\n if (profile) {\n return {\n userId: profile.userId,\n phoneNumber: profile.phoneNumber,\n accountNo: this.accountNo ?? undefined,\n };\n }\n\n // Fallback to session data\n if (this.session) {\n return {\n userId: String(this.session.userId),\n phoneNumber: this.session.phoneNumber,\n accountNo: this.accountNo ?? undefined,\n };\n }\n\n throw new AuthError('Failed to fetch account info');\n }\n\n /**\n * Get user profile\n * Note: /user API may not be available, returns basic info from session\n */\n async getUserProfile(): Promise<UserProfile> {\n await this.ensureAuthenticated();\n\n // Try to fetch from API first\n const profile = await fetchUserProfile(this.httpClient, this.getSessionHeaders());\n\n if (profile) {\n return profile;\n }\n\n // Fallback to session data\n if (this.session) {\n return {\n userId: String(this.session.userId),\n phoneNumber: this.session.phoneNumber,\n };\n }\n\n throw new AuthError('Failed to fetch user profile');\n }\n\n /**\n * Ensure client is authenticated, login if needed\n */\n private async ensureAuthenticated(): Promise<void> {\n if (!this.session) {\n await this.login();\n }\n }\n\n /**\n * Get session headers for API requests\n */\n private getSessionHeaders() {\n if (!this.session) {\n throw new AuthError('Not authenticated');\n }\n\n return buildSessionHeaders(\n this.session.token,\n this.session.timoDeviceId,\n this.session.contextId,\n this.credentials.browserSignature\n );\n }\n\n /**\n * Fetch and store account number from /user/accountPreview\n * Falls back to provided accountNo in options if API fails\n */\n private async fetchAccountNumber(): Promise<void> {\n // Skip if already set via options\n if (this.accountNo) {\n this.logger?.debug('Using provided account number', { accountNo: this.accountNo });\n return;\n }\n\n try {\n const accountNo = await fetchSpendAccountNo(this.httpClient, this.getSessionHeaders());\n if (accountNo) {\n this.accountNo = accountNo;\n this.logger?.debug('Account number fetched from API', { accountNo });\n } else {\n this.logger?.warn('No Spend Account found. Provide accountNo in options.');\n }\n } catch {\n this.logger?.warn('Failed to fetch account number. Provide accountNo in options.');\n }\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * OS detection for browser signature\n */\nfunction getOSName(): string {\n const platform = process.platform;\n switch (platform) {\n case 'win32':\n return 'Windows';\n case 'darwin':\n return 'MacOS';\n case 'linux':\n return 'Linux';\n default:\n return 'Windows';\n }\n}\n\n/**\n * Browser name based on OS\n */\nfunction getBrowserName(): string {\n const os = getOSName();\n return os === 'MacOS' ? 'safari' : 'chrome';\n}\n\n/**\n * Generate a device fingerprint ID\n */\nexport function generateDeviceId(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * Build browser signature for current platform\n * Format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport function buildBrowserSignature(): string {\n const os = getOSName();\n const browser = getBrowserName();\n return `:WEB:${os}:297:WEB:desktop:${browser}`;\n}\n\n/**\n * Get default browser signature (Windows Chrome)\n */\nexport function getDefaultBrowserSignature(): string {\n return DEFAULT_BROWSER_SIGNATURE;\n}\n\n/**\n * Device credentials used for authentication\n */\nexport interface DeviceCredentials {\n deviceId: string;\n browserSignature: string;\n}\n\n/**\n * Generate new device credentials\n */\nexport function generateDeviceCredentials(): DeviceCredentials {\n return {\n deviceId: generateDeviceId(),\n browserSignature: buildBrowserSignature(),\n };\n}\n","/**\n * @timo-bank/core\n * Unofficial Timo Bank SDK\n */\n\nexport const VERSION = '0.1.0';\n\n// Main client\nexport { TimoClient } from './client.js';\n\n// Types\nexport * from './types/index.js';\n\n// Errors\nexport * from './errors/index.js';\n\n// Credentials\nexport * from './credentials/index.js';\n\n// Utils (selective exports)\nexport { sha512, generateUUID } from './utils/crypto.js';\nexport { API_CODES, ACCOUNT_TYPE_SPEND } from './utils/constants.js';\n\n// HTTP\nexport { HttpClient } from './http/client.js';\nexport type { RequestOptions } from './http/client.js';\nexport {\n buildDeviceReg,\n buildContextId,\n buildDeviceKey,\n buildAuthHeaders,\n buildSessionHeaders,\n} from './http/headers.js';\nexport type { AuthHeaders, SessionHeaders } from './http/headers.js';\n\n// Auth\nexport {\n generateDeviceId,\n buildBrowserSignature,\n generateDeviceCredentials,\n} from './auth/device.js';\nexport type { DeviceCredentials } from './auth/device.js';\nexport { LoginManager } from './auth/login.js';\nexport type { SessionData, OtpRequirement, LoginResult } from './auth/login.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/constants.ts","../src/errors/classes.ts","../src/http/client.ts","../src/utils/crypto.ts","../src/http/headers.ts","../src/auth/login.ts","../src/credentials/encoder.ts","../src/credentials/parser.ts","../src/api/balance.ts","../src/api/transaction.ts","../src/api/account.ts","../src/client.ts","../src/auth/device.ts","../src/index.ts"],"names":["formatDate"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * API configuration constants\n */\nexport const API_BASE_URL = 'https://app2.timo.vn';\nexport const API_HOSTNAME = 'app2.timo.vn';\n\n/**\n * Default browser signature format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport const DEFAULT_BROWSER_SIGNATURE = ':WEB:Windows:297:WEB:desktop:chrome';\n\n/**\n * Encryption key seed for AES encryption of device ID.\n * This is derived from Timo's public web app and is not a secret.\n * It's used to match the behavior of the official web client.\n */\nexport const ENCRYPTION_KEY_SEED = 'uuidKeyT1m0@412NTMK';\n\n/**\n * API response codes\n */\nexport const API_CODES = {\n SUCCESS: 200,\n UNAUTHORIZED: 401,\n OTP_REQUIRED: 6001,\n TWO_FACTOR_REQUIRED: 6006,\n} as const;\n\n/**\n * Default HTTP headers\n */\nexport const DEFAULT_HEADERS = {\n accept: 'application/json, text/plain, */*',\n 'content-type': 'application/json; charset=UTF-8',\n origin: 'https://my.timo.vn',\n referer: 'https://my.timo.vn/',\n 'user-agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',\n} as const;\n\n/**\n * Spend account type code\n */\nexport const ACCOUNT_TYPE_SPEND = '1025';\n","/**\n * Base error class for all Timo SDK errors\n */\nexport class TimoError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimoError';\n Object.setPrototypeOf(this, TimoError.prototype);\n }\n}\n\n/**\n * Authentication related errors\n */\nexport class AuthError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n Object.setPrototypeOf(this, AuthError.prototype);\n }\n}\n\n/**\n * Invalid or malformed credential token\n */\nexport class CredentialError extends TimoError {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialError';\n Object.setPrototypeOf(this, CredentialError.prototype);\n }\n}\n\n/**\n * Session has expired, needs re-login\n */\nexport class SessionExpiredError extends TimoError {\n constructor(message = 'Session expired') {\n super(message);\n this.name = 'SessionExpiredError';\n Object.setPrototypeOf(this, SessionExpiredError.prototype);\n }\n}\n\n/**\n * Device has been invalidated by Timo, needs new setup\n */\nexport class DeviceInvalidatedError extends TimoError {\n constructor(message = 'Device invalidated, run setup again') {\n super(message);\n this.name = 'DeviceInvalidatedError';\n Object.setPrototypeOf(this, DeviceInvalidatedError.prototype);\n }\n}\n\n/**\n * API request failed\n */\nexport class ApiError extends TimoError {\n public readonly code: number;\n public readonly response?: unknown;\n\n constructor(message: string, code: number, response?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.code = code;\n this.response = response;\n Object.setPrototypeOf(this, ApiError.prototype);\n }\n}\n","import * as https from 'node:https';\nimport { API_HOSTNAME, DEFAULT_HEADERS } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\nimport type { ApiResponse } from '../types/api.js';\n\nexport interface RequestOptions {\n headers?: Record<string, string>;\n timeout?: number;\n}\n\n/**\n * HTTP client for Timo API requests\n */\nexport class HttpClient {\n private readonly hostname: string;\n\n constructor(hostname: string = API_HOSTNAME) {\n this.hostname = hostname;\n }\n\n /**\n * Make a POST request\n */\n async post<T = unknown>(\n path: string,\n body: object,\n options: RequestOptions = {}\n ): Promise<ApiResponse<T>> {\n const data = JSON.stringify(body);\n const headers = {\n ...DEFAULT_HEADERS,\n 'content-length': Buffer.byteLength(data).toString(),\n ...options.headers,\n };\n\n return this.request<T>('POST', path, headers, data, options.timeout);\n }\n\n /**\n * Make a GET request\n */\n async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<ApiResponse<T>> {\n // Remove content-type for GET requests\n const { 'content-type': _contentType, ...getHeaders } = DEFAULT_HEADERS;\n const headers = {\n ...getHeaders,\n ...options.headers,\n };\n\n return this.request<T>('GET', path, headers, undefined, options.timeout);\n }\n\n private request<T>(\n method: string,\n path: string,\n headers: Record<string, string>,\n body?: string,\n timeout = 30000\n ): Promise<ApiResponse<T>> {\n return new Promise((resolve, reject) => {\n const requestOptions = {\n hostname: this.hostname,\n port: 443,\n path,\n method,\n headers,\n timeout,\n };\n\n const req = https.request(requestOptions, (res) => {\n let responseData = '';\n\n res.on('data', (chunk) => {\n responseData += chunk;\n });\n\n res.on('end', () => {\n try {\n const parsed = JSON.parse(responseData) as ApiResponse<T>;\n resolve(parsed);\n } catch {\n reject(new ApiError('Failed to parse response', res.statusCode || 500, responseData));\n }\n });\n });\n\n req.on('error', (error) => {\n reject(new ApiError(`Request failed: ${error.message}`, 0));\n });\n\n req.on('timeout', () => {\n req.destroy();\n reject(new ApiError('Request timeout', 408));\n });\n\n if (body) {\n req.write(body);\n }\n\n req.end();\n });\n }\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { ENCRYPTION_KEY_SEED } from './constants.js';\n\n/**\n * Generate SHA256 hash\n */\nexport function sha256(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Generate SHA512 hash\n */\nexport function sha512(input: string): string {\n return createHash('sha512').update(input).digest('hex');\n}\n\n/**\n * Get derived encryption key (first 16 chars of SHA256 of seed)\n */\nexport function getEncryptionKey(): string {\n return sha256(ENCRYPTION_KEY_SEED).substring(0, 16);\n}\n\n/**\n * Generate a cryptographically secure UUID v4\n */\nexport function generateUUID(): string {\n return randomUUID();\n}\n","import { sha256, generateUUID } from '../utils/crypto.js';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * Build device registration header value\n * Format: deviceId + browserSignature\n */\nexport function buildDeviceReg(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${deviceId}${signature}`;\n}\n\n/**\n * Build context ID header value\n * Format: sha256(WEB.deviceId.297).uuid\n */\nexport function buildContextId(deviceId: string): string {\n const inputString = `WEB.${deviceId}.297`;\n const hashPart = sha256(inputString);\n const uuid = generateUUID();\n return `${hashPart}.${uuid}`;\n}\n\n/**\n * Build device key header value (used after login)\n * Format: timoDeviceId + browserSignature\n */\nexport function buildDeviceKey(\n timoDeviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): string {\n return `${timoDeviceId}${signature}`;\n}\n\n/**\n * Build authentication headers for API requests\n */\nexport interface AuthHeaders {\n 'x-timo-devicereg': string;\n 'x-gofs-context-id': string;\n [key: string]: string;\n}\n\nexport function buildAuthHeaders(\n deviceId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): AuthHeaders {\n return {\n 'x-timo-devicereg': buildDeviceReg(deviceId, signature),\n 'x-gofs-context-id': buildContextId(deviceId),\n };\n}\n\n/**\n * Build headers for authenticated requests (after login)\n */\nexport interface SessionHeaders {\n token: string;\n 'x-gofs-context-id': string;\n 'x-timo-devicekey': string;\n [key: string]: string;\n}\n\nexport function buildSessionHeaders(\n token: string,\n timoDeviceId: string,\n contextId: string,\n signature: string = DEFAULT_BROWSER_SIGNATURE\n): SessionHeaders {\n return {\n token,\n 'x-gofs-context-id': contextId,\n 'x-timo-devicekey': buildDeviceKey(timoDeviceId, signature),\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport { buildAuthHeaders, buildContextId } from '../http/headers.js';\nimport { API_CODES } from '../utils/constants.js';\nimport { AuthError, SessionExpiredError, DeviceInvalidatedError } from '../errors/classes.js';\nimport type { ApiResponse, LoginResponseData } from '../types/api.js';\n\n/**\n * Session data after successful login\n */\nexport interface SessionData {\n token: string;\n userId: string;\n phoneNumber: string;\n timoDeviceId: string;\n contextId: string;\n}\n\n/**\n * OTP requirement response\n */\nexport interface OtpRequirement {\n type: 'device' | 'twoFactor';\n refNo: string;\n token: string;\n}\n\n/**\n * Login result - either success or OTP required\n */\nexport type LoginResult =\n | { success: true; session: SessionData }\n | { success: false; otpRequired: OtpRequirement };\n\n/**\n * Login manager for handling authentication flow\n */\nexport class LoginManager {\n private readonly httpClient: HttpClient;\n private readonly deviceId: string;\n private readonly browserSignature: string;\n private readonly contextId: string;\n\n constructor(\n deviceId: string,\n browserSignature: string,\n httpClient: HttpClient = new HttpClient()\n ) {\n this.deviceId = deviceId;\n this.browserSignature = browserSignature;\n this.httpClient = httpClient;\n this.contextId = buildContextId(deviceId);\n }\n\n /**\n * Perform initial login\n * @param username - Phone number\n * @param password - Pre-hashed password (SHA512)\n */\n async login(username: string, password: string): Promise<LoginResult> {\n const authHeaders = buildAuthHeaders(this.deviceId, this.browserSignature);\n\n const response = await this.httpClient.post<LoginResponseData>(\n '/login',\n {\n username,\n password,\n lang: 'vn',\n },\n { headers: authHeaders }\n );\n\n return this.handleLoginResponse(response);\n }\n\n /**\n * Verify device OTP (code 6001)\n */\n async verifyDeviceOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n /**\n * Verify two-factor OTP (code 6006)\n */\n async verifyTwoFactorOtp(otp: string, refNo: string, token: string): Promise<SessionData> {\n const response = await this.httpClient.post<LoginResponseData>(\n '/login/afs/commit',\n {\n lang: 'vn',\n refNo,\n otp,\n securityChallenge: otp,\n securityCode: otp,\n smsOTP: otp,\n },\n {\n headers: {\n ...buildAuthHeaders(this.deviceId, this.browserSignature),\n token,\n },\n }\n );\n\n return this.handleOtpResponse(response);\n }\n\n private handleLoginResponse(response: ApiResponse<LoginResponseData>): LoginResult {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return {\n success: true,\n session: this.buildSessionData(response.data),\n };\n }\n\n if (response.code === API_CODES.OTP_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'device',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n if (response.code === API_CODES.TWO_FACTOR_REQUIRED && response.data) {\n return {\n success: false,\n otpRequired: {\n type: 'twoFactor',\n refNo: response.data.refNo || '',\n token: response.data.token,\n },\n };\n }\n\n // Handle 401 - Invalid credentials\n if (response.code === API_CODES.UNAUTHORIZED) {\n throw new AuthError('Invalid phone number or password');\n }\n\n throw new AuthError(response.message || 'Login failed');\n }\n\n private handleOtpResponse(response: ApiResponse<LoginResponseData>): SessionData {\n if (response.success && response.code === API_CODES.SUCCESS && response.data) {\n return this.buildSessionData(response.data);\n }\n\n // Check for device invalidation\n if (response.message?.includes('device') && response.message?.includes('invalid')) {\n throw new DeviceInvalidatedError();\n }\n\n // Check for session expiry\n if (response.message?.includes('session') && response.message?.includes('expired')) {\n throw new SessionExpiredError();\n }\n\n throw new AuthError(response.message || 'OTP verification failed');\n }\n\n private buildSessionData(data: LoginResponseData): SessionData {\n if (!data.timoDeviceId) {\n throw new AuthError('Missing timoDeviceId in response');\n }\n\n return {\n token: data.token,\n userId: data.userId,\n phoneNumber: data.phoneNumber,\n timoDeviceId: data.timoDeviceId,\n contextId: this.contextId,\n };\n }\n\n /**\n * Get the context ID for this login session\n */\n getContextId(): string {\n return this.contextId;\n }\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\n\nexport const TOKEN_PREFIX = 'timo_v1_';\nexport const TOKEN_VERSION = 'v1';\n\n/**\n * Encode credential data to a token string.\n * Note: Base64 encoding is NOT encryption - tokens should be treated as secrets.\n */\nexport function encodeCredentials(data: CredentialData): string {\n const json = JSON.stringify(data);\n const base64 = Buffer.from(json, 'utf-8').toString('base64');\n return `${TOKEN_PREFIX}${base64}`;\n}\n\n/**\n * Decode a credential token to credential data\n */\nexport function decodeCredentials(token: string): CredentialData {\n if (!token.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError('Invalid credential token format');\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n const data = JSON.parse(json) as CredentialData;\n\n // Validate all required fields\n if (\n !data.username ||\n !data.password ||\n !data.deviceId ||\n !data.timoDeviceId ||\n !data.browserSignature ||\n !data.createdAt\n ) {\n throw new CredentialError('Invalid credential data: missing required fields');\n }\n\n return data;\n } catch (error) {\n if (error instanceof CredentialError) {\n throw error;\n }\n throw new CredentialError('Failed to decode credential token');\n }\n}\n\n/**\n * Validate a credential token format without fully decoding\n */\nexport function isValidTokenFormat(token: string): boolean {\n if (!token.startsWith(TOKEN_PREFIX)) {\n return false;\n }\n\n const base64 = token.slice(TOKEN_PREFIX.length);\n\n try {\n const json = Buffer.from(base64, 'base64').toString('utf-8');\n JSON.parse(json);\n return true;\n } catch {\n return false;\n }\n}\n","import type { CredentialData } from '../types/config.js';\nimport { CredentialError } from '../errors/classes.js';\nimport { TOKEN_PREFIX, decodeCredentials } from './encoder.js';\n\n/**\n * Extract version from token\n */\nexport function getTokenVersion(token: string): string {\n if (token.startsWith(TOKEN_PREFIX)) {\n return 'v1';\n }\n throw new CredentialError('Unsupported credential version');\n}\n\n/**\n * Parse and validate a credential token\n */\nexport function parseCredentialToken(token: string): CredentialData {\n if (!token || typeof token !== 'string') {\n throw new CredentialError('Credential token is required');\n }\n\n const trimmed = token.trim();\n\n if (!trimmed.startsWith(TOKEN_PREFIX)) {\n throw new CredentialError(\n `Invalid credential token format. Expected token starting with \"${TOKEN_PREFIX}\"`\n );\n }\n\n // Validate version\n getTokenVersion(trimmed);\n\n // Decode and validate\n return decodeCredentials(trimmed);\n}\n\n/**\n * Mask sensitive data in credentials for safe logging\n */\nexport function maskCredentials(data: CredentialData): Record<string, string> {\n return {\n username: maskPhone(data.username),\n password: '***',\n deviceId: maskString(data.deviceId),\n timoDeviceId: maskString(data.timoDeviceId),\n browserSignature: maskBrowserSignature(data.browserSignature),\n createdAt: data.createdAt,\n };\n}\n\nfunction maskBrowserSignature(sig: string): string {\n // Browser signature format: :WEB:OS:VERSION:WEB:device:browser\n // Keep format visible but mask specific values\n const parts = sig.split(':');\n if (parts.length >= 6) {\n return `:${parts[1]}:***:***:${parts[4]}:***:***`;\n }\n return '***';\n}\n\nfunction maskPhone(phone: string): string {\n if (phone.length <= 4) return '****';\n return phone.slice(0, 4) + '*'.repeat(phone.length - 4);\n}\n\nfunction maskString(str: string): string {\n if (str.length <= 8) return '****';\n return str.slice(0, 4) + '...' + str.slice(-4);\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { Balance } from '../types/models.js';\nimport type { TransactionListResponseData } from '../types/api.js';\nimport { ACCOUNT_TYPE_SPEND } from '../utils/constants.js';\nimport { ApiError } from '../errors/classes.js';\n\n/**\n * Format a date as DD/MM/YYYY for Timo API\n */\nfunction formatDate(date: Date): string {\n const day = String(date.getDate()).padStart(2, '0');\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const year = date.getFullYear();\n return `${day}/${month}/${year}`;\n}\n\n/**\n * Fetch account balance via transaction list endpoint\n */\nexport async function fetchBalance(\n httpClient: HttpClient,\n accountNo: string,\n sessionHeaders: SessionHeaders\n): Promise<Balance> {\n // Use transaction list endpoint to get balance info\n const today = new Date();\n const payload = {\n format: 'group',\n index: 0,\n offset: -1,\n accountNo,\n accountType: ACCOUNT_TYPE_SPEND,\n fromDate: formatDate(today),\n toDate: formatDate(today),\n };\n\n const response = await httpClient.post<TransactionListResponseData>(\n '/user/account/transaction/list',\n payload,\n { headers: sessionHeaders }\n );\n\n if (!response.success || !response.data) {\n throw new ApiError('Failed to fetch balance', response.code);\n }\n\n return {\n accountNo,\n accountBalance: response.data.accountBalance ?? 0,\n availableAmount: response.data.availableAmount ?? 0,\n };\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { Transaction, TransactionOptions } from '../types/models.js';\nimport type { TransactionListResponseData, TransactionItem } from '../types/api.js';\nimport { ACCOUNT_TYPE_SPEND } from '../utils/constants.js';\n\n/**\n * Format a date as DD/MM/YYYY for Timo API\n */\nfunction formatDate(date: Date): string {\n const day = String(date.getDate()).padStart(2, '0');\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const year = date.getFullYear();\n return `${day}/${month}/${year}`;\n}\n\n/**\n * Get default date range (last 30 days)\n */\nfunction getDefaultDateRange(): { fromDate: string; toDate: string } {\n const today = new Date();\n const thirtyDaysAgo = new Date(today);\n thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);\n\n return {\n fromDate: formatDate(thirtyDaysAgo),\n toDate: formatDate(today),\n };\n}\n\n/**\n * Map API transaction item to Transaction model\n */\nfunction mapTransaction(item: TransactionItem): Transaction {\n return {\n refNo: item.refNo,\n txnAmount: item.txnAmount,\n txnDesc: item.txnDesc,\n txnTime: item.txnTime,\n txnType: item.txnType,\n transactionTime: item.transactionTime,\n txnBankName: item.txnBankName,\n txnBankAccount: item.txnBankAccount,\n };\n}\n\n/**\n * Fetch transaction history\n */\nexport async function fetchTransactions(\n httpClient: HttpClient,\n accountNo: string,\n sessionHeaders: SessionHeaders,\n options?: TransactionOptions\n): Promise<Transaction[]> {\n const dateRange = getDefaultDateRange();\n\n const payload = {\n format: 'group',\n index: 0,\n offset: -1,\n accountNo,\n accountType: ACCOUNT_TYPE_SPEND,\n fromDate: options?.fromDate ?? dateRange.fromDate,\n toDate: options?.toDate ?? dateRange.toDate,\n };\n\n const response = await httpClient.post<TransactionListResponseData>(\n '/user/account/transaction/list',\n payload,\n { headers: sessionHeaders }\n );\n\n if (!response.success || !response.data) {\n return [];\n }\n\n // Flatten grouped transactions\n const allTransactions: Transaction[] = [];\n for (const group of response.data.items ?? []) {\n for (const item of group.item ?? []) {\n allTransactions.push(mapTransaction(item));\n }\n }\n\n // Apply limit if specified\n if (options?.limit && options.limit > 0) {\n return allTransactions.slice(0, options.limit);\n }\n\n return allTransactions;\n}\n","import { HttpClient } from '../http/client.js';\nimport type { SessionHeaders } from '../http/headers.js';\nimport type { UserProfile, Account, AccountType } from '../types/models.js';\n\n/** Account type mapping from API codes */\nconst ACCOUNT_TYPE_MAP: Record<string, AccountType> = {\n '1025': 'spend',\n '1026': 'goal',\n '1027': 'term',\n '1028': 'money_market',\n '9999': 'credit',\n '1910': 'split_bill',\n '6511': 'insurance',\n};\n\ninterface AccountPreviewItem {\n no: string;\n accountType: string;\n name: string;\n balance?: number;\n accountBalance?: number;\n availableAmount?: number;\n}\n\ninterface AccountPreviewResponse {\n accounts: AccountPreviewItem[];\n}\n\ninterface ProfileResponseData {\n userId: number;\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n legalId?: string;\n cityName?: string;\n address?: string;\n birthday?: string;\n gender?: string;\n}\n\n/**\n * Fetch user profile from /user/getProfile\n * Note: accountNo is not available from this API - must be provided via options\n */\nexport async function fetchUserProfile(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<UserProfile | null> {\n const response = await httpClient.get<ProfileResponseData>('/user/getProfile', {\n headers: sessionHeaders,\n });\n\n if (!response.success || !response.data) {\n return null;\n }\n\n const data = response.data;\n const fullName = `${data.firstName} ${data.lastName}`.trim();\n\n return {\n userId: String(data.userId),\n phoneNumber: data.phone,\n fullName: fullName || undefined,\n email: data.email,\n };\n}\n\n/**\n * Fetch all accounts from /user/accountPreview\n * Returns list of all user accounts (spend, goal, term, etc.)\n */\nexport async function fetchAccounts(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<Account[]> {\n const response = await httpClient.get<AccountPreviewResponse>('/user/accountPreview', {\n headers: sessionHeaders,\n });\n\n if (!response.success || !response.data?.accounts) {\n return [];\n }\n\n return response.data.accounts\n .filter((item) => item.no) // Only include accounts with a number\n .map((item) => ({\n no: item.no,\n accountType: item.accountType,\n type: ACCOUNT_TYPE_MAP[item.accountType] ?? 'spend',\n name: item.name,\n balance: item.balance,\n accountBalance: item.accountBalance,\n availableAmount: item.availableAmount,\n }));\n}\n\n/**\n * Fetch spend account number from /user/accountPreview\n * Returns the main Spend Account number (accountType: 1025)\n */\nexport async function fetchSpendAccountNo(\n httpClient: HttpClient,\n sessionHeaders: SessionHeaders\n): Promise<string | null> {\n const accounts = await fetchAccounts(httpClient, sessionHeaders);\n const spendAccount = accounts.find((a) => a.type === 'spend');\n return spendAccount?.no ?? null;\n}\n","import { HttpClient } from './http/client.js';\nimport { buildSessionHeaders } from './http/headers.js';\nimport { LoginManager } from './auth/login.js';\nimport type { SessionData } from './auth/login.js';\nimport { parseCredentialToken } from './credentials/parser.js';\nimport { fetchBalance } from './api/balance.js';\nimport { fetchTransactions } from './api/transaction.js';\nimport { fetchUserProfile, fetchSpendAccountNo } from './api/account.js';\nimport type { TimoClientOptions, CredentialData, Logger } from './types/config.js';\nimport type { Balance, Transaction, TransactionOptions, AccountInfo, UserProfile } from './types/models.js';\nimport { AuthError } from './errors/classes.js';\n\n/**\n * Main Timo Bank client\n */\nexport class TimoClient {\n private readonly credentials: CredentialData;\n private readonly httpClient: HttpClient;\n private readonly logger?: Logger;\n private session: SessionData | null = null;\n private accountNo: string | null = null;\n\n constructor(options: TimoClientOptions) {\n this.credentials = parseCredentialToken(options.credentials);\n this.httpClient = new HttpClient();\n this.logger = options.logger;\n this.accountNo = options.accountNo ?? null;\n }\n\n /**\n * Login to Timo Bank\n * Uses saved device credentials for automatic login without OTP\n */\n async login(): Promise<void> {\n const loginManager = new LoginManager(\n this.credentials.deviceId,\n this.credentials.browserSignature,\n this.httpClient\n );\n\n // Login using saved credentials (should not require OTP with valid timoDeviceId)\n const result = await loginManager.login(this.credentials.username, this.credentials.password);\n\n if (!result.success) {\n throw new AuthError(\n `Device requires re-authorization. OTP type: ${result.otpRequired.type}. ` +\n 'Run \"npx @timo-bank/core setup\" to re-authorize this device.'\n );\n }\n\n this.session = result.session;\n this.logger?.info('Login successful', { userId: this.session.userId });\n\n // Fetch account number for API calls\n await this.fetchAccountNumber();\n }\n\n /**\n * Logout and clear session\n */\n async logout(): Promise<void> {\n this.session = null;\n this.accountNo = null;\n this.logger?.info('Logged out');\n }\n\n /**\n * Check if client is authenticated\n */\n isAuthenticated(): boolean {\n return this.session !== null;\n }\n\n /**\n * Get account balance\n */\n async getBalance(): Promise<Balance> {\n await this.ensureAuthenticated();\n\n if (!this.accountNo) {\n throw new AuthError('Account number not available');\n }\n\n return fetchBalance(this.httpClient, this.accountNo, this.getSessionHeaders());\n }\n\n /**\n * Get transaction history\n */\n async getTransactions(options?: TransactionOptions): Promise<Transaction[]> {\n await this.ensureAuthenticated();\n\n if (!this.accountNo) {\n throw new AuthError('Account number not available');\n }\n\n return fetchTransactions(this.httpClient, this.accountNo, this.getSessionHeaders(), options);\n }\n\n /**\n * Get account information\n * Uses fetchUserProfile internally, adds accountNo from options/session\n */\n async getAccountInfo(): Promise<AccountInfo> {\n await this.ensureAuthenticated();\n\n // Try to fetch from API first\n const profile = await fetchUserProfile(this.httpClient, this.getSessionHeaders());\n\n if (profile) {\n return {\n userId: profile.userId,\n phoneNumber: profile.phoneNumber,\n accountNo: this.accountNo ?? undefined,\n };\n }\n\n // Fallback to session data\n if (this.session) {\n return {\n userId: String(this.session.userId),\n phoneNumber: this.session.phoneNumber,\n accountNo: this.accountNo ?? undefined,\n };\n }\n\n throw new AuthError('Failed to fetch account info');\n }\n\n /**\n * Get user profile\n * Note: /user API may not be available, returns basic info from session\n */\n async getUserProfile(): Promise<UserProfile> {\n await this.ensureAuthenticated();\n\n // Try to fetch from API first\n const profile = await fetchUserProfile(this.httpClient, this.getSessionHeaders());\n\n if (profile) {\n return profile;\n }\n\n // Fallback to session data\n if (this.session) {\n return {\n userId: String(this.session.userId),\n phoneNumber: this.session.phoneNumber,\n };\n }\n\n throw new AuthError('Failed to fetch user profile');\n }\n\n /**\n * Ensure client is authenticated, login if needed\n */\n private async ensureAuthenticated(): Promise<void> {\n if (!this.session) {\n await this.login();\n }\n }\n\n /**\n * Get session headers for API requests\n */\n private getSessionHeaders() {\n if (!this.session) {\n throw new AuthError('Not authenticated');\n }\n\n return buildSessionHeaders(\n this.session.token,\n this.session.timoDeviceId,\n this.session.contextId,\n this.credentials.browserSignature\n );\n }\n\n /**\n * Fetch and store account number from /user/accountPreview\n * Falls back to provided accountNo in options if API fails\n */\n private async fetchAccountNumber(): Promise<void> {\n // Skip if already set via options\n if (this.accountNo) {\n this.logger?.debug('Using provided account number', { accountNo: this.accountNo });\n return;\n }\n\n try {\n const accountNo = await fetchSpendAccountNo(this.httpClient, this.getSessionHeaders());\n if (accountNo) {\n this.accountNo = accountNo;\n this.logger?.debug('Account number fetched from API', { accountNo });\n } else {\n this.logger?.warn('No Spend Account found. Provide accountNo in options.');\n }\n } catch {\n this.logger?.warn('Failed to fetch account number. Provide accountNo in options.');\n }\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { DEFAULT_BROWSER_SIGNATURE } from '../utils/constants.js';\n\n/**\n * OS detection for browser signature\n */\nfunction getOSName(): string {\n const platform = process.platform;\n switch (platform) {\n case 'win32':\n return 'Windows';\n case 'darwin':\n return 'MacOS';\n case 'linux':\n return 'Linux';\n default:\n return 'Windows';\n }\n}\n\n/**\n * Browser name based on OS\n */\nfunction getBrowserName(): string {\n const os = getOSName();\n return os === 'MacOS' ? 'safari' : 'chrome';\n}\n\n/**\n * Generate a device fingerprint ID\n */\nexport function generateDeviceId(): string {\n return randomBytes(16).toString('hex');\n}\n\n/**\n * Build browser signature for current platform\n * Format: :WEB:OS:VERSION:WEB:device:browser\n */\nexport function buildBrowserSignature(): string {\n const os = getOSName();\n const browser = getBrowserName();\n return `:WEB:${os}:297:WEB:desktop:${browser}`;\n}\n\n/**\n * Get default browser signature (Windows Chrome)\n */\nexport function getDefaultBrowserSignature(): string {\n return DEFAULT_BROWSER_SIGNATURE;\n}\n\n/**\n * Device credentials used for authentication\n */\nexport interface DeviceCredentials {\n deviceId: string;\n browserSignature: string;\n}\n\n/**\n * Generate new device credentials\n */\nexport function generateDeviceCredentials(): DeviceCredentials {\n return {\n deviceId: generateDeviceId(),\n browserSignature: buildBrowserSignature(),\n };\n}\n","/**\n * @timo-bank/core\n * Unofficial Timo Bank SDK\n */\n\nexport const VERSION = '0.1.0';\n\n// Main client\nexport { TimoClient } from './client.js';\n\n// Types\nexport * from './types/index.js';\n\n// Errors\nexport * from './errors/index.js';\n\n// Credentials\nexport * from './credentials/index.js';\n\n// Utils (selective exports)\nexport { sha512, generateUUID } from './utils/crypto.js';\nexport { API_CODES, ACCOUNT_TYPE_SPEND } from './utils/constants.js';\n\n// HTTP\nexport { HttpClient } from './http/client.js';\nexport type { RequestOptions } from './http/client.js';\nexport {\n buildDeviceReg,\n buildContextId,\n buildDeviceKey,\n buildAuthHeaders,\n buildSessionHeaders,\n} from './http/headers.js';\nexport type { AuthHeaders, SessionHeaders } from './http/headers.js';\n\n// Auth\nexport {\n generateDeviceId,\n buildBrowserSignature,\n generateDeviceCredentials,\n} from './auth/device.js';\nexport type { DeviceCredentials } from './auth/device.js';\nexport { LoginManager } from './auth/login.js';\nexport type { SessionData, OtpRequirement, LoginResult } from './auth/login.js';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timo-bank/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Unofficial Timo Bank SDK - Core package",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -19,16 +19,6 @@
|
|
|
19
19
|
"dist",
|
|
20
20
|
"bin"
|
|
21
21
|
],
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "tsup",
|
|
24
|
-
"dev": "tsup --watch",
|
|
25
|
-
"clean": "rimraf dist",
|
|
26
|
-
"typecheck": "tsc --noEmit",
|
|
27
|
-
"test": "vitest run",
|
|
28
|
-
"test:watch": "vitest",
|
|
29
|
-
"test:unit": "vitest run tests/unit",
|
|
30
|
-
"test:integration": "vitest run tests/integration"
|
|
31
|
-
},
|
|
32
22
|
"keywords": [
|
|
33
23
|
"timo",
|
|
34
24
|
"bank",
|
|
@@ -59,5 +49,15 @@
|
|
|
59
49
|
},
|
|
60
50
|
"engines": {
|
|
61
51
|
"node": ">=18.0.0"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"dev": "tsup --watch",
|
|
56
|
+
"clean": "rimraf dist",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"test:watch": "vitest",
|
|
60
|
+
"test:unit": "vitest run tests/unit",
|
|
61
|
+
"test:integration": "vitest run tests/integration"
|
|
62
62
|
}
|
|
63
|
-
}
|
|
63
|
+
}
|