@akira-io/billing-js 0.3.2 → 0.3.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/dist/desktop.cjs +70 -0
- package/dist/desktop.cjs.map +1 -1
- package/dist/desktop.d.cts +26 -2
- package/dist/desktop.d.ts +26 -2
- package/dist/desktop.js +69 -2
- package/dist/desktop.js.map +1 -1
- package/package.json +1 -1
package/dist/desktop.cjs
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var child_process = require('child_process');
|
|
4
4
|
var crypto = require('crypto');
|
|
5
|
+
var promises = require('fs/promises');
|
|
6
|
+
var path = require('path');
|
|
5
7
|
var os = require('os');
|
|
6
8
|
|
|
7
9
|
// src/desktop/checkout.ts
|
|
@@ -31,6 +33,71 @@ function openBrowser(url) {
|
|
|
31
33
|
resolve();
|
|
32
34
|
});
|
|
33
35
|
}
|
|
36
|
+
var NONCE_LEN = 12;
|
|
37
|
+
var TokenCipher = class {
|
|
38
|
+
key;
|
|
39
|
+
constructor(key) {
|
|
40
|
+
if (key.length !== 32) throw new Error("TokenCipher: key must be 32 bytes");
|
|
41
|
+
this.key = crypto.webcrypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
42
|
+
}
|
|
43
|
+
async encrypt(plaintext) {
|
|
44
|
+
const key = await this.key;
|
|
45
|
+
const nonce = crypto.webcrypto.getRandomValues(new Uint8Array(NONCE_LEN));
|
|
46
|
+
const ct = new Uint8Array(await crypto.webcrypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, key, new TextEncoder().encode(plaintext)));
|
|
47
|
+
const combined = new Uint8Array(NONCE_LEN + ct.length);
|
|
48
|
+
combined.set(nonce, 0);
|
|
49
|
+
combined.set(ct, NONCE_LEN);
|
|
50
|
+
return Buffer.from(combined).toString("base64");
|
|
51
|
+
}
|
|
52
|
+
async decrypt(encoded) {
|
|
53
|
+
const bytes = new Uint8Array(Buffer.from(encoded, "base64"));
|
|
54
|
+
if (bytes.length <= NONCE_LEN) throw new Error("ciphertext too short");
|
|
55
|
+
const nonce = bytes.slice(0, NONCE_LEN);
|
|
56
|
+
const ct = bytes.slice(NONCE_LEN);
|
|
57
|
+
const key = await this.key;
|
|
58
|
+
const pt = new Uint8Array(await crypto.webcrypto.subtle.decrypt({ name: "AES-GCM", iv: nonce }, key, ct));
|
|
59
|
+
return new TextDecoder().decode(pt);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
function generateKey() {
|
|
63
|
+
return crypto.webcrypto.getRandomValues(new Uint8Array(32));
|
|
64
|
+
}
|
|
65
|
+
var KeyStore = class {
|
|
66
|
+
constructor(opts) {
|
|
67
|
+
this.opts = opts;
|
|
68
|
+
}
|
|
69
|
+
opts;
|
|
70
|
+
async loadOrCreate() {
|
|
71
|
+
if (this.opts.debugFilePath) {
|
|
72
|
+
try {
|
|
73
|
+
const encoded2 = (await promises.readFile(this.opts.debugFilePath, "utf8")).trim();
|
|
74
|
+
return decodeKey(encoded2);
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const encoded = await this.opts.keyring.get().catch(() => null);
|
|
79
|
+
if (encoded) {
|
|
80
|
+
const key2 = decodeKey(encoded);
|
|
81
|
+
await this.writeDebugFile(encoded);
|
|
82
|
+
return key2;
|
|
83
|
+
}
|
|
84
|
+
const key = generateKey();
|
|
85
|
+
const encodedNew = Buffer.from(key).toString("base64");
|
|
86
|
+
await this.opts.keyring.set(encodedNew).catch(() => void 0);
|
|
87
|
+
await this.writeDebugFile(encodedNew);
|
|
88
|
+
return key;
|
|
89
|
+
}
|
|
90
|
+
async writeDebugFile(encoded) {
|
|
91
|
+
if (!this.opts.debugFilePath) return;
|
|
92
|
+
await promises.mkdir(path.dirname(this.opts.debugFilePath), { recursive: true });
|
|
93
|
+
await promises.writeFile(this.opts.debugFilePath, encoded, "utf8");
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
function decodeKey(encoded) {
|
|
97
|
+
const bytes = new Uint8Array(Buffer.from(encoded, "base64"));
|
|
98
|
+
if (bytes.length !== 32) throw new Error("key has unexpected length");
|
|
99
|
+
return bytes;
|
|
100
|
+
}
|
|
34
101
|
async function machineId() {
|
|
35
102
|
const dyn = new Function("m", "return import(m)");
|
|
36
103
|
try {
|
|
@@ -189,10 +256,13 @@ var AuthController = class {
|
|
|
189
256
|
};
|
|
190
257
|
|
|
191
258
|
exports.AuthController = AuthController;
|
|
259
|
+
exports.KeyStore = KeyStore;
|
|
192
260
|
exports.SessionStore = SessionStore;
|
|
261
|
+
exports.TokenCipher = TokenCipher;
|
|
193
262
|
exports.TokenKeyring = TokenKeyring;
|
|
194
263
|
exports.checkoutUrl = checkoutUrl;
|
|
195
264
|
exports.deviceFingerprint = deviceFingerprint;
|
|
265
|
+
exports.generateKey = generateKey;
|
|
196
266
|
exports.openBrowser = openBrowser;
|
|
197
267
|
//# sourceMappingURL=desktop.cjs.map
|
|
198
268
|
//# sourceMappingURL=desktop.cjs.map
|
package/dist/desktop.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/desktop/checkout.ts","../src/desktop/browser.ts","../src/desktop/fingerprint.ts","../src/desktop/keyring.ts","../src/desktop/session.ts","../src/desktop/use-auth.ts"],"names":["spawn","hostname","platform","createHash"],"mappings":"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","file":"desktop.cjs","sourcesContent":["/** Build the public checkout URL for a product on the billing site. */\nexport function checkoutUrl(baseUrl: string, product: string): string {\n return `${baseUrl.replace(/\\/$/, '')}/plans?product=${encodeURIComponent(product)}`;\n}\n","import { spawn } from 'node:child_process';\n\n/** Launch the system default browser. Node-only. */\nexport function openBrowser(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n let cmd: string;\n let args: string[];\n switch (process.platform) {\n case 'darwin':\n cmd = 'open';\n args = [url];\n break;\n case 'win32':\n cmd = 'rundll32';\n args = ['url.dll,FileProtocolHandler', url];\n break;\n default:\n cmd = 'xdg-open';\n args = [url];\n }\n const child = spawn(cmd, args, { stdio: 'ignore', detached: true });\n child.once('error', reject);\n child.unref();\n resolve();\n });\n}\n","import { createHash } from 'node:crypto';\nimport { hostname, platform } from 'node:os';\n\nexport interface DeviceFingerprint {\n fingerprint: string;\n platform: string;\n app_version: string;\n}\n\nasync function machineId(): Promise<string> {\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n try {\n const mod = (await dyn('node-machine-id').catch(() => null)) as { machineIdSync?: (orig?: boolean) => string } | null;\n if (mod?.machineIdSync) return mod.machineIdSync(true);\n } catch {\n // fallthrough\n }\n return hostname();\n}\n\nexport async function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint> {\n const id = await machineId();\n const plat = platform();\n const hash = createHash('sha256');\n hash.update(id);\n hash.update('::');\n hash.update(plat);\n hash.update('::');\n hash.update(appVersion);\n return {\n fingerprint: hash.digest('hex'),\n platform: plat,\n app_version: appVersion,\n };\n}\n","/**\n * Thin wrapper around `keytar` for desktop apps. Consumers install `keytar` as\n * an optional dependency; this module dynamically imports it so server-side\n * bundles never see it.\n */\nexport interface TokenKeyringOptions {\n service: string;\n account: string;\n}\n\ntype Keytar = {\n getPassword: (service: string, account: string) => Promise<string | null>;\n setPassword: (service: string, account: string, password: string) => Promise<void>;\n deletePassword: (service: string, account: string) => Promise<boolean>;\n};\n\nasync function loadKeytar(): Promise<Keytar> {\n // Use Function constructor so tsup/typescript don't try to resolve the\n // optional module at build time. Consumers add `keytar` as a peer/optional\n // dependency.\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n const mod = (await dyn('keytar').catch(() => null)) as { default?: Keytar } | null;\n if (!mod) throw new Error('TokenKeyring: install `keytar` to use the desktop keyring helper.');\n return mod.default ?? (mod as unknown as Keytar);\n}\n\nexport class TokenKeyring {\n private readonly service: string;\n private readonly account: string;\n\n constructor(opts: TokenKeyringOptions) {\n this.service = opts.service;\n this.account = opts.account;\n }\n\n async get(): Promise<string | null> {\n const k = await loadKeytar();\n return k.getPassword(this.service, this.account);\n }\n\n async set(value: string): Promise<void> {\n const k = await loadKeytar();\n await k.setPassword(this.service, this.account, value);\n }\n\n async delete(): Promise<void> {\n const k = await loadKeytar();\n await k.deletePassword(this.service, this.account);\n }\n}\n","import { BillingClient } from '../client';\nimport { TokenKeyring } from './keyring';\n\nexport class SessionStore {\n constructor(private readonly keyring: TokenKeyring) {}\n\n async hydrate(client: BillingClient): Promise<boolean> {\n const token = await this.keyring.get();\n if (!token) return false;\n client.setCustomerToken(token);\n return true;\n }\n\n async persist(client: BillingClient, token: string): Promise<void> {\n await this.keyring.set(token);\n client.setCustomerToken(token);\n }\n\n async clear(client: BillingClient): Promise<void> {\n await this.keyring.delete();\n client.setCustomerToken('');\n }\n\n async hasToken(): Promise<boolean> {\n const v = await this.keyring.get();\n return !!v;\n }\n}\n","import type { BillingClient } from '../client';\nimport type { Customer, OauthExchangeResponse } from '../client-types';\nimport type { LoopbackOutcome } from '../loopback';\nimport type { SessionStore } from './session';\n\nexport type AuthStatusState =\n | { state: 'loading' }\n | { state: 'guest' }\n | { state: 'authenticated'; customer: Customer; licensed: boolean };\n\nexport interface AuthControllerOptions {\n client: BillingClient;\n session: SessionStore;\n product: string;\n feature?: string;\n /** Loopback flow used by `oauthLogin`. */\n loopbackLogin: (provider: string) => Promise<LoopbackOutcome>;\n /** Override the runtime license check (e.g. cache, custom feature key). */\n checkLicense?: (client: BillingClient) => Promise<boolean>;\n}\n\nexport class AuthController {\n private listeners = new Set<(s: AuthStatusState) => void>();\n private current: AuthStatusState = { state: 'loading' };\n\n constructor(private readonly opts: AuthControllerOptions) {}\n\n subscribe(listener: (s: AuthStatusState) => void): () => void {\n this.listeners.add(listener);\n listener(this.current);\n return () => this.listeners.delete(listener);\n }\n\n snapshot(): AuthStatusState {\n return this.current;\n }\n\n private emit(next: AuthStatusState): void {\n this.current = next;\n this.listeners.forEach((l) => l(next));\n }\n\n async bootstrap(): Promise<void> {\n await this.opts.session.hydrate(this.opts.client);\n await this.refresh();\n }\n\n async refresh(): Promise<void> {\n if (!(await this.opts.session.hasToken())) {\n this.emit({ state: 'guest' });\n return;\n }\n try {\n const customer = await this.opts.client.customerMe();\n const licensed = this.opts.checkLicense\n ? await this.opts.checkLicense(this.opts.client)\n : await this.runDefaultLicenseCheck();\n this.emit({ state: 'authenticated', customer, licensed });\n } catch (e) {\n if ((e as { status?: number }).status === 401) {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n return;\n }\n throw e;\n }\n }\n\n private async runDefaultLicenseCheck(): Promise<boolean> {\n try {\n const resp = await this.opts.client.licenseCheck({\n product: this.opts.product,\n feature: this.opts.feature ?? 'general',\n });\n return resp.allowed;\n } catch {\n return false;\n }\n }\n\n async requestOtp(email: string, deviceFp?: string): Promise<void> {\n await this.opts.client.requestOtp({ email, device_fp: deviceFp ?? '' });\n }\n\n async verifyOtp(email: string, code: string, deviceFp?: string): Promise<Customer> {\n const resp = await this.opts.client.verifyOtp({\n email,\n code,\n device_fp: deviceFp ?? '',\n });\n await this.opts.session.persist(this.opts.client, resp.access_token);\n await this.refresh();\n return (await this.opts.client.customerMe());\n }\n\n async oauthLogin(provider: string): Promise<OauthExchangeResponse> {\n const outcome = await this.opts.loopbackLogin(provider);\n await this.opts.session.persist(this.opts.client, outcome.exchange.access_token);\n await this.refresh();\n return outcome.exchange;\n }\n\n async logout(): Promise<void> {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/desktop/checkout.ts","../src/desktop/browser.ts","../src/desktop/cipher.ts","../src/desktop/keystore.ts","../src/desktop/fingerprint.ts","../src/desktop/keyring.ts","../src/desktop/session.ts","../src/desktop/use-auth.ts"],"names":["spawn","webcrypto","encoded","readFile","key","mkdir","dirname","writeFile","hostname","platform","createHash"],"mappings":"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","file":"desktop.cjs","sourcesContent":["/** Build the public checkout URL for a product on the billing site. */\nexport function checkoutUrl(baseUrl: string, product: string): string {\n return `${baseUrl.replace(/\\/$/, '')}/plans?product=${encodeURIComponent(product)}`;\n}\n","import { spawn } from 'node:child_process';\n\n/** Launch the system default browser. Node-only. */\nexport function openBrowser(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n let cmd: string;\n let args: string[];\n switch (process.platform) {\n case 'darwin':\n cmd = 'open';\n args = [url];\n break;\n case 'win32':\n cmd = 'rundll32';\n args = ['url.dll,FileProtocolHandler', url];\n break;\n default:\n cmd = 'xdg-open';\n args = [url];\n }\n const child = spawn(cmd, args, { stdio: 'ignore', detached: true });\n child.once('error', reject);\n child.unref();\n resolve();\n });\n}\n","import { webcrypto } from 'node:crypto';\n\nconst NONCE_LEN = 12;\n\n/** AES-256-GCM string encryption keyed by a 32-byte secret. */\nexport class TokenCipher {\n private readonly key: CryptoKey | Promise<CryptoKey>;\n\n constructor(key: Uint8Array) {\n if (key.length !== 32) throw new Error('TokenCipher: key must be 32 bytes');\n this.key = webcrypto.subtle.importKey('raw', key as unknown as ArrayBuffer, 'AES-GCM', false, ['encrypt', 'decrypt']);\n }\n\n async encrypt(plaintext: string): Promise<string> {\n const key = await this.key;\n const nonce = webcrypto.getRandomValues(new Uint8Array(NONCE_LEN));\n const ct = new Uint8Array(await webcrypto.subtle.encrypt({ name: 'AES-GCM', iv: nonce }, key, new TextEncoder().encode(plaintext)));\n const combined = new Uint8Array(NONCE_LEN + ct.length);\n combined.set(nonce, 0);\n combined.set(ct, NONCE_LEN);\n return Buffer.from(combined).toString('base64');\n }\n\n async decrypt(encoded: string): Promise<string> {\n const bytes = new Uint8Array(Buffer.from(encoded, 'base64'));\n if (bytes.length <= NONCE_LEN) throw new Error('ciphertext too short');\n const nonce = bytes.slice(0, NONCE_LEN);\n const ct = bytes.slice(NONCE_LEN);\n const key = await this.key;\n const pt = new Uint8Array(await webcrypto.subtle.decrypt({ name: 'AES-GCM', iv: nonce }, key, ct as unknown as ArrayBuffer));\n return new TextDecoder().decode(pt);\n }\n}\n\nexport function generateKey(): Uint8Array {\n return webcrypto.getRandomValues(new Uint8Array(32));\n}\n","import { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nimport { generateKey } from './cipher';\nimport { TokenKeyring } from './keyring';\n\nexport interface KeyStoreOptions {\n keyring: TokenKeyring;\n /** Optional debug fallback path. When set, the key is mirrored to disk so\n * unsigned dev rebuilds can still decrypt previously persisted data. */\n debugFilePath?: string;\n}\n\n/** Loads or generates a 32-byte AES key, persisted in the OS keychain. */\nexport class KeyStore {\n constructor(private readonly opts: KeyStoreOptions) {}\n\n async loadOrCreate(): Promise<Uint8Array> {\n if (this.opts.debugFilePath) {\n try {\n const encoded = (await readFile(this.opts.debugFilePath, 'utf8')).trim();\n return decodeKey(encoded);\n } catch { /* fall through */ }\n }\n\n const encoded = await this.opts.keyring.get().catch(() => null);\n if (encoded) {\n const key = decodeKey(encoded);\n await this.writeDebugFile(encoded);\n return key;\n }\n\n const key = generateKey();\n const encodedNew = Buffer.from(key).toString('base64');\n await this.opts.keyring.set(encodedNew).catch(() => undefined);\n await this.writeDebugFile(encodedNew);\n return key;\n }\n\n private async writeDebugFile(encoded: string): Promise<void> {\n if (!this.opts.debugFilePath) return;\n await mkdir(dirname(this.opts.debugFilePath), { recursive: true });\n await writeFile(this.opts.debugFilePath, encoded, 'utf8');\n }\n}\n\nfunction decodeKey(encoded: string): Uint8Array {\n const bytes = new Uint8Array(Buffer.from(encoded, 'base64'));\n if (bytes.length !== 32) throw new Error('key has unexpected length');\n return bytes;\n}\n","import { createHash } from 'node:crypto';\nimport { hostname, platform } from 'node:os';\n\nexport interface DeviceFingerprint {\n fingerprint: string;\n platform: string;\n app_version: string;\n}\n\nasync function machineId(): Promise<string> {\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n try {\n const mod = (await dyn('node-machine-id').catch(() => null)) as { machineIdSync?: (orig?: boolean) => string } | null;\n if (mod?.machineIdSync) return mod.machineIdSync(true);\n } catch {\n // fallthrough\n }\n return hostname();\n}\n\nexport async function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint> {\n const id = await machineId();\n const plat = platform();\n const hash = createHash('sha256');\n hash.update(id);\n hash.update('::');\n hash.update(plat);\n hash.update('::');\n hash.update(appVersion);\n return {\n fingerprint: hash.digest('hex'),\n platform: plat,\n app_version: appVersion,\n };\n}\n","/**\n * Thin wrapper around `keytar` for desktop apps. Consumers install `keytar` as\n * an optional dependency; this module dynamically imports it so server-side\n * bundles never see it.\n */\nexport interface TokenKeyringOptions {\n service: string;\n account: string;\n}\n\ntype Keytar = {\n getPassword: (service: string, account: string) => Promise<string | null>;\n setPassword: (service: string, account: string, password: string) => Promise<void>;\n deletePassword: (service: string, account: string) => Promise<boolean>;\n};\n\nasync function loadKeytar(): Promise<Keytar> {\n // Use Function constructor so tsup/typescript don't try to resolve the\n // optional module at build time. Consumers add `keytar` as a peer/optional\n // dependency.\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n const mod = (await dyn('keytar').catch(() => null)) as { default?: Keytar } | null;\n if (!mod) throw new Error('TokenKeyring: install `keytar` to use the desktop keyring helper.');\n return mod.default ?? (mod as unknown as Keytar);\n}\n\nexport class TokenKeyring {\n private readonly service: string;\n private readonly account: string;\n\n constructor(opts: TokenKeyringOptions) {\n this.service = opts.service;\n this.account = opts.account;\n }\n\n async get(): Promise<string | null> {\n const k = await loadKeytar();\n return k.getPassword(this.service, this.account);\n }\n\n async set(value: string): Promise<void> {\n const k = await loadKeytar();\n await k.setPassword(this.service, this.account, value);\n }\n\n async delete(): Promise<void> {\n const k = await loadKeytar();\n await k.deletePassword(this.service, this.account);\n }\n}\n","import { BillingClient } from '../client';\nimport { TokenKeyring } from './keyring';\n\nexport class SessionStore {\n constructor(private readonly keyring: TokenKeyring) {}\n\n async hydrate(client: BillingClient): Promise<boolean> {\n const token = await this.keyring.get();\n if (!token) return false;\n client.setCustomerToken(token);\n return true;\n }\n\n async persist(client: BillingClient, token: string): Promise<void> {\n await this.keyring.set(token);\n client.setCustomerToken(token);\n }\n\n async clear(client: BillingClient): Promise<void> {\n await this.keyring.delete();\n client.setCustomerToken('');\n }\n\n async hasToken(): Promise<boolean> {\n const v = await this.keyring.get();\n return !!v;\n }\n}\n","import type { BillingClient } from '../client';\nimport type { Customer, OauthExchangeResponse } from '../client-types';\nimport type { LoopbackOutcome } from '../loopback';\nimport type { SessionStore } from './session';\n\nexport type AuthStatusState =\n | { state: 'loading' }\n | { state: 'guest' }\n | { state: 'authenticated'; customer: Customer; licensed: boolean };\n\nexport interface AuthControllerOptions {\n client: BillingClient;\n session: SessionStore;\n product: string;\n feature?: string;\n /** Loopback flow used by `oauthLogin`. */\n loopbackLogin: (provider: string) => Promise<LoopbackOutcome>;\n /** Override the runtime license check (e.g. cache, custom feature key). */\n checkLicense?: (client: BillingClient) => Promise<boolean>;\n}\n\nexport class AuthController {\n private listeners = new Set<(s: AuthStatusState) => void>();\n private current: AuthStatusState = { state: 'loading' };\n\n constructor(private readonly opts: AuthControllerOptions) {}\n\n subscribe(listener: (s: AuthStatusState) => void): () => void {\n this.listeners.add(listener);\n listener(this.current);\n return () => this.listeners.delete(listener);\n }\n\n snapshot(): AuthStatusState {\n return this.current;\n }\n\n private emit(next: AuthStatusState): void {\n this.current = next;\n this.listeners.forEach((l) => l(next));\n }\n\n async bootstrap(): Promise<void> {\n await this.opts.session.hydrate(this.opts.client);\n await this.refresh();\n }\n\n async refresh(): Promise<void> {\n if (!(await this.opts.session.hasToken())) {\n this.emit({ state: 'guest' });\n return;\n }\n try {\n const customer = await this.opts.client.customerMe();\n const licensed = this.opts.checkLicense\n ? await this.opts.checkLicense(this.opts.client)\n : await this.runDefaultLicenseCheck();\n this.emit({ state: 'authenticated', customer, licensed });\n } catch (e) {\n if ((e as { status?: number }).status === 401) {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n return;\n }\n throw e;\n }\n }\n\n private async runDefaultLicenseCheck(): Promise<boolean> {\n try {\n const resp = await this.opts.client.licenseCheck({\n product: this.opts.product,\n feature: this.opts.feature ?? 'general',\n });\n return resp.allowed;\n } catch {\n return false;\n }\n }\n\n async requestOtp(email: string, deviceFp?: string): Promise<void> {\n await this.opts.client.requestOtp({ email, device_fp: deviceFp ?? '' });\n }\n\n async verifyOtp(email: string, code: string, deviceFp?: string): Promise<Customer> {\n const resp = await this.opts.client.verifyOtp({\n email,\n code,\n device_fp: deviceFp ?? '',\n });\n await this.opts.session.persist(this.opts.client, resp.access_token);\n await this.refresh();\n return (await this.opts.client.customerMe());\n }\n\n async oauthLogin(provider: string): Promise<OauthExchangeResponse> {\n const outcome = await this.opts.loopbackLogin(provider);\n await this.opts.session.persist(this.opts.client, outcome.exchange.access_token);\n await this.refresh();\n return outcome.exchange;\n }\n\n async logout(): Promise<void> {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n }\n}\n"]}
|
package/dist/desktop.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { T as TokenKeyring } from './use-auth-Dbol539E.cjs';
|
|
2
|
+
export { A as AuthController, a as AuthControllerOptions, b as AuthStatusState, S as SessionStore, c as TokenKeyringOptions } from './use-auth-Dbol539E.cjs';
|
|
2
3
|
import './client.cjs';
|
|
3
4
|
import './client-types-BRHIoFm6.cjs';
|
|
4
5
|
import './loopback.cjs';
|
|
@@ -9,6 +10,29 @@ declare function checkoutUrl(baseUrl: string, product: string): string;
|
|
|
9
10
|
/** Launch the system default browser. Node-only. */
|
|
10
11
|
declare function openBrowser(url: string): Promise<void>;
|
|
11
12
|
|
|
13
|
+
/** AES-256-GCM string encryption keyed by a 32-byte secret. */
|
|
14
|
+
declare class TokenCipher {
|
|
15
|
+
private readonly key;
|
|
16
|
+
constructor(key: Uint8Array);
|
|
17
|
+
encrypt(plaintext: string): Promise<string>;
|
|
18
|
+
decrypt(encoded: string): Promise<string>;
|
|
19
|
+
}
|
|
20
|
+
declare function generateKey(): Uint8Array;
|
|
21
|
+
|
|
22
|
+
interface KeyStoreOptions {
|
|
23
|
+
keyring: TokenKeyring;
|
|
24
|
+
/** Optional debug fallback path. When set, the key is mirrored to disk so
|
|
25
|
+
* unsigned dev rebuilds can still decrypt previously persisted data. */
|
|
26
|
+
debugFilePath?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Loads or generates a 32-byte AES key, persisted in the OS keychain. */
|
|
29
|
+
declare class KeyStore {
|
|
30
|
+
private readonly opts;
|
|
31
|
+
constructor(opts: KeyStoreOptions);
|
|
32
|
+
loadOrCreate(): Promise<Uint8Array>;
|
|
33
|
+
private writeDebugFile;
|
|
34
|
+
}
|
|
35
|
+
|
|
12
36
|
interface DeviceFingerprint {
|
|
13
37
|
fingerprint: string;
|
|
14
38
|
platform: string;
|
|
@@ -16,4 +40,4 @@ interface DeviceFingerprint {
|
|
|
16
40
|
}
|
|
17
41
|
declare function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint>;
|
|
18
42
|
|
|
19
|
-
export { type DeviceFingerprint, checkoutUrl, deviceFingerprint, openBrowser };
|
|
43
|
+
export { type DeviceFingerprint, KeyStore, type KeyStoreOptions, TokenCipher, TokenKeyring, checkoutUrl, deviceFingerprint, generateKey, openBrowser };
|
package/dist/desktop.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { T as TokenKeyring } from './use-auth-BaAeVSZO.js';
|
|
2
|
+
export { A as AuthController, a as AuthControllerOptions, b as AuthStatusState, S as SessionStore, c as TokenKeyringOptions } from './use-auth-BaAeVSZO.js';
|
|
2
3
|
import './client.js';
|
|
3
4
|
import './client-types-BRHIoFm6.js';
|
|
4
5
|
import './loopback.js';
|
|
@@ -9,6 +10,29 @@ declare function checkoutUrl(baseUrl: string, product: string): string;
|
|
|
9
10
|
/** Launch the system default browser. Node-only. */
|
|
10
11
|
declare function openBrowser(url: string): Promise<void>;
|
|
11
12
|
|
|
13
|
+
/** AES-256-GCM string encryption keyed by a 32-byte secret. */
|
|
14
|
+
declare class TokenCipher {
|
|
15
|
+
private readonly key;
|
|
16
|
+
constructor(key: Uint8Array);
|
|
17
|
+
encrypt(plaintext: string): Promise<string>;
|
|
18
|
+
decrypt(encoded: string): Promise<string>;
|
|
19
|
+
}
|
|
20
|
+
declare function generateKey(): Uint8Array;
|
|
21
|
+
|
|
22
|
+
interface KeyStoreOptions {
|
|
23
|
+
keyring: TokenKeyring;
|
|
24
|
+
/** Optional debug fallback path. When set, the key is mirrored to disk so
|
|
25
|
+
* unsigned dev rebuilds can still decrypt previously persisted data. */
|
|
26
|
+
debugFilePath?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Loads or generates a 32-byte AES key, persisted in the OS keychain. */
|
|
29
|
+
declare class KeyStore {
|
|
30
|
+
private readonly opts;
|
|
31
|
+
constructor(opts: KeyStoreOptions);
|
|
32
|
+
loadOrCreate(): Promise<Uint8Array>;
|
|
33
|
+
private writeDebugFile;
|
|
34
|
+
}
|
|
35
|
+
|
|
12
36
|
interface DeviceFingerprint {
|
|
13
37
|
fingerprint: string;
|
|
14
38
|
platform: string;
|
|
@@ -16,4 +40,4 @@ interface DeviceFingerprint {
|
|
|
16
40
|
}
|
|
17
41
|
declare function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint>;
|
|
18
42
|
|
|
19
|
-
export { type DeviceFingerprint, checkoutUrl, deviceFingerprint, openBrowser };
|
|
43
|
+
export { type DeviceFingerprint, KeyStore, type KeyStoreOptions, TokenCipher, TokenKeyring, checkoutUrl, deviceFingerprint, generateKey, openBrowser };
|
package/dist/desktop.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
-
import { createHash } from 'crypto';
|
|
2
|
+
import { webcrypto, createHash } from 'crypto';
|
|
3
|
+
import { readFile, mkdir, writeFile } from 'fs/promises';
|
|
4
|
+
import { dirname } from 'path';
|
|
3
5
|
import { platform, hostname } from 'os';
|
|
4
6
|
|
|
5
7
|
// src/desktop/checkout.ts
|
|
@@ -29,6 +31,71 @@ function openBrowser(url) {
|
|
|
29
31
|
resolve();
|
|
30
32
|
});
|
|
31
33
|
}
|
|
34
|
+
var NONCE_LEN = 12;
|
|
35
|
+
var TokenCipher = class {
|
|
36
|
+
key;
|
|
37
|
+
constructor(key) {
|
|
38
|
+
if (key.length !== 32) throw new Error("TokenCipher: key must be 32 bytes");
|
|
39
|
+
this.key = webcrypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
40
|
+
}
|
|
41
|
+
async encrypt(plaintext) {
|
|
42
|
+
const key = await this.key;
|
|
43
|
+
const nonce = webcrypto.getRandomValues(new Uint8Array(NONCE_LEN));
|
|
44
|
+
const ct = new Uint8Array(await webcrypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, key, new TextEncoder().encode(plaintext)));
|
|
45
|
+
const combined = new Uint8Array(NONCE_LEN + ct.length);
|
|
46
|
+
combined.set(nonce, 0);
|
|
47
|
+
combined.set(ct, NONCE_LEN);
|
|
48
|
+
return Buffer.from(combined).toString("base64");
|
|
49
|
+
}
|
|
50
|
+
async decrypt(encoded) {
|
|
51
|
+
const bytes = new Uint8Array(Buffer.from(encoded, "base64"));
|
|
52
|
+
if (bytes.length <= NONCE_LEN) throw new Error("ciphertext too short");
|
|
53
|
+
const nonce = bytes.slice(0, NONCE_LEN);
|
|
54
|
+
const ct = bytes.slice(NONCE_LEN);
|
|
55
|
+
const key = await this.key;
|
|
56
|
+
const pt = new Uint8Array(await webcrypto.subtle.decrypt({ name: "AES-GCM", iv: nonce }, key, ct));
|
|
57
|
+
return new TextDecoder().decode(pt);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
function generateKey() {
|
|
61
|
+
return webcrypto.getRandomValues(new Uint8Array(32));
|
|
62
|
+
}
|
|
63
|
+
var KeyStore = class {
|
|
64
|
+
constructor(opts) {
|
|
65
|
+
this.opts = opts;
|
|
66
|
+
}
|
|
67
|
+
opts;
|
|
68
|
+
async loadOrCreate() {
|
|
69
|
+
if (this.opts.debugFilePath) {
|
|
70
|
+
try {
|
|
71
|
+
const encoded2 = (await readFile(this.opts.debugFilePath, "utf8")).trim();
|
|
72
|
+
return decodeKey(encoded2);
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const encoded = await this.opts.keyring.get().catch(() => null);
|
|
77
|
+
if (encoded) {
|
|
78
|
+
const key2 = decodeKey(encoded);
|
|
79
|
+
await this.writeDebugFile(encoded);
|
|
80
|
+
return key2;
|
|
81
|
+
}
|
|
82
|
+
const key = generateKey();
|
|
83
|
+
const encodedNew = Buffer.from(key).toString("base64");
|
|
84
|
+
await this.opts.keyring.set(encodedNew).catch(() => void 0);
|
|
85
|
+
await this.writeDebugFile(encodedNew);
|
|
86
|
+
return key;
|
|
87
|
+
}
|
|
88
|
+
async writeDebugFile(encoded) {
|
|
89
|
+
if (!this.opts.debugFilePath) return;
|
|
90
|
+
await mkdir(dirname(this.opts.debugFilePath), { recursive: true });
|
|
91
|
+
await writeFile(this.opts.debugFilePath, encoded, "utf8");
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
function decodeKey(encoded) {
|
|
95
|
+
const bytes = new Uint8Array(Buffer.from(encoded, "base64"));
|
|
96
|
+
if (bytes.length !== 32) throw new Error("key has unexpected length");
|
|
97
|
+
return bytes;
|
|
98
|
+
}
|
|
32
99
|
async function machineId() {
|
|
33
100
|
const dyn = new Function("m", "return import(m)");
|
|
34
101
|
try {
|
|
@@ -186,6 +253,6 @@ var AuthController = class {
|
|
|
186
253
|
}
|
|
187
254
|
};
|
|
188
255
|
|
|
189
|
-
export { AuthController, SessionStore, TokenKeyring, checkoutUrl, deviceFingerprint, openBrowser };
|
|
256
|
+
export { AuthController, KeyStore, SessionStore, TokenCipher, TokenKeyring, checkoutUrl, deviceFingerprint, generateKey, openBrowser };
|
|
190
257
|
//# sourceMappingURL=desktop.js.map
|
|
191
258
|
//# sourceMappingURL=desktop.js.map
|
package/dist/desktop.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/desktop/checkout.ts","../src/desktop/browser.ts","../src/desktop/fingerprint.ts","../src/desktop/keyring.ts","../src/desktop/session.ts","../src/desktop/use-auth.ts"],"names":[],"mappings":"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","file":"desktop.js","sourcesContent":["/** Build the public checkout URL for a product on the billing site. */\nexport function checkoutUrl(baseUrl: string, product: string): string {\n return `${baseUrl.replace(/\\/$/, '')}/plans?product=${encodeURIComponent(product)}`;\n}\n","import { spawn } from 'node:child_process';\n\n/** Launch the system default browser. Node-only. */\nexport function openBrowser(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n let cmd: string;\n let args: string[];\n switch (process.platform) {\n case 'darwin':\n cmd = 'open';\n args = [url];\n break;\n case 'win32':\n cmd = 'rundll32';\n args = ['url.dll,FileProtocolHandler', url];\n break;\n default:\n cmd = 'xdg-open';\n args = [url];\n }\n const child = spawn(cmd, args, { stdio: 'ignore', detached: true });\n child.once('error', reject);\n child.unref();\n resolve();\n });\n}\n","import { createHash } from 'node:crypto';\nimport { hostname, platform } from 'node:os';\n\nexport interface DeviceFingerprint {\n fingerprint: string;\n platform: string;\n app_version: string;\n}\n\nasync function machineId(): Promise<string> {\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n try {\n const mod = (await dyn('node-machine-id').catch(() => null)) as { machineIdSync?: (orig?: boolean) => string } | null;\n if (mod?.machineIdSync) return mod.machineIdSync(true);\n } catch {\n // fallthrough\n }\n return hostname();\n}\n\nexport async function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint> {\n const id = await machineId();\n const plat = platform();\n const hash = createHash('sha256');\n hash.update(id);\n hash.update('::');\n hash.update(plat);\n hash.update('::');\n hash.update(appVersion);\n return {\n fingerprint: hash.digest('hex'),\n platform: plat,\n app_version: appVersion,\n };\n}\n","/**\n * Thin wrapper around `keytar` for desktop apps. Consumers install `keytar` as\n * an optional dependency; this module dynamically imports it so server-side\n * bundles never see it.\n */\nexport interface TokenKeyringOptions {\n service: string;\n account: string;\n}\n\ntype Keytar = {\n getPassword: (service: string, account: string) => Promise<string | null>;\n setPassword: (service: string, account: string, password: string) => Promise<void>;\n deletePassword: (service: string, account: string) => Promise<boolean>;\n};\n\nasync function loadKeytar(): Promise<Keytar> {\n // Use Function constructor so tsup/typescript don't try to resolve the\n // optional module at build time. Consumers add `keytar` as a peer/optional\n // dependency.\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n const mod = (await dyn('keytar').catch(() => null)) as { default?: Keytar } | null;\n if (!mod) throw new Error('TokenKeyring: install `keytar` to use the desktop keyring helper.');\n return mod.default ?? (mod as unknown as Keytar);\n}\n\nexport class TokenKeyring {\n private readonly service: string;\n private readonly account: string;\n\n constructor(opts: TokenKeyringOptions) {\n this.service = opts.service;\n this.account = opts.account;\n }\n\n async get(): Promise<string | null> {\n const k = await loadKeytar();\n return k.getPassword(this.service, this.account);\n }\n\n async set(value: string): Promise<void> {\n const k = await loadKeytar();\n await k.setPassword(this.service, this.account, value);\n }\n\n async delete(): Promise<void> {\n const k = await loadKeytar();\n await k.deletePassword(this.service, this.account);\n }\n}\n","import { BillingClient } from '../client';\nimport { TokenKeyring } from './keyring';\n\nexport class SessionStore {\n constructor(private readonly keyring: TokenKeyring) {}\n\n async hydrate(client: BillingClient): Promise<boolean> {\n const token = await this.keyring.get();\n if (!token) return false;\n client.setCustomerToken(token);\n return true;\n }\n\n async persist(client: BillingClient, token: string): Promise<void> {\n await this.keyring.set(token);\n client.setCustomerToken(token);\n }\n\n async clear(client: BillingClient): Promise<void> {\n await this.keyring.delete();\n client.setCustomerToken('');\n }\n\n async hasToken(): Promise<boolean> {\n const v = await this.keyring.get();\n return !!v;\n }\n}\n","import type { BillingClient } from '../client';\nimport type { Customer, OauthExchangeResponse } from '../client-types';\nimport type { LoopbackOutcome } from '../loopback';\nimport type { SessionStore } from './session';\n\nexport type AuthStatusState =\n | { state: 'loading' }\n | { state: 'guest' }\n | { state: 'authenticated'; customer: Customer; licensed: boolean };\n\nexport interface AuthControllerOptions {\n client: BillingClient;\n session: SessionStore;\n product: string;\n feature?: string;\n /** Loopback flow used by `oauthLogin`. */\n loopbackLogin: (provider: string) => Promise<LoopbackOutcome>;\n /** Override the runtime license check (e.g. cache, custom feature key). */\n checkLicense?: (client: BillingClient) => Promise<boolean>;\n}\n\nexport class AuthController {\n private listeners = new Set<(s: AuthStatusState) => void>();\n private current: AuthStatusState = { state: 'loading' };\n\n constructor(private readonly opts: AuthControllerOptions) {}\n\n subscribe(listener: (s: AuthStatusState) => void): () => void {\n this.listeners.add(listener);\n listener(this.current);\n return () => this.listeners.delete(listener);\n }\n\n snapshot(): AuthStatusState {\n return this.current;\n }\n\n private emit(next: AuthStatusState): void {\n this.current = next;\n this.listeners.forEach((l) => l(next));\n }\n\n async bootstrap(): Promise<void> {\n await this.opts.session.hydrate(this.opts.client);\n await this.refresh();\n }\n\n async refresh(): Promise<void> {\n if (!(await this.opts.session.hasToken())) {\n this.emit({ state: 'guest' });\n return;\n }\n try {\n const customer = await this.opts.client.customerMe();\n const licensed = this.opts.checkLicense\n ? await this.opts.checkLicense(this.opts.client)\n : await this.runDefaultLicenseCheck();\n this.emit({ state: 'authenticated', customer, licensed });\n } catch (e) {\n if ((e as { status?: number }).status === 401) {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n return;\n }\n throw e;\n }\n }\n\n private async runDefaultLicenseCheck(): Promise<boolean> {\n try {\n const resp = await this.opts.client.licenseCheck({\n product: this.opts.product,\n feature: this.opts.feature ?? 'general',\n });\n return resp.allowed;\n } catch {\n return false;\n }\n }\n\n async requestOtp(email: string, deviceFp?: string): Promise<void> {\n await this.opts.client.requestOtp({ email, device_fp: deviceFp ?? '' });\n }\n\n async verifyOtp(email: string, code: string, deviceFp?: string): Promise<Customer> {\n const resp = await this.opts.client.verifyOtp({\n email,\n code,\n device_fp: deviceFp ?? '',\n });\n await this.opts.session.persist(this.opts.client, resp.access_token);\n await this.refresh();\n return (await this.opts.client.customerMe());\n }\n\n async oauthLogin(provider: string): Promise<OauthExchangeResponse> {\n const outcome = await this.opts.loopbackLogin(provider);\n await this.opts.session.persist(this.opts.client, outcome.exchange.access_token);\n await this.refresh();\n return outcome.exchange;\n }\n\n async logout(): Promise<void> {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/desktop/checkout.ts","../src/desktop/browser.ts","../src/desktop/cipher.ts","../src/desktop/keystore.ts","../src/desktop/fingerprint.ts","../src/desktop/keyring.ts","../src/desktop/session.ts","../src/desktop/use-auth.ts"],"names":["encoded","key"],"mappings":"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","file":"desktop.js","sourcesContent":["/** Build the public checkout URL for a product on the billing site. */\nexport function checkoutUrl(baseUrl: string, product: string): string {\n return `${baseUrl.replace(/\\/$/, '')}/plans?product=${encodeURIComponent(product)}`;\n}\n","import { spawn } from 'node:child_process';\n\n/** Launch the system default browser. Node-only. */\nexport function openBrowser(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n let cmd: string;\n let args: string[];\n switch (process.platform) {\n case 'darwin':\n cmd = 'open';\n args = [url];\n break;\n case 'win32':\n cmd = 'rundll32';\n args = ['url.dll,FileProtocolHandler', url];\n break;\n default:\n cmd = 'xdg-open';\n args = [url];\n }\n const child = spawn(cmd, args, { stdio: 'ignore', detached: true });\n child.once('error', reject);\n child.unref();\n resolve();\n });\n}\n","import { webcrypto } from 'node:crypto';\n\nconst NONCE_LEN = 12;\n\n/** AES-256-GCM string encryption keyed by a 32-byte secret. */\nexport class TokenCipher {\n private readonly key: CryptoKey | Promise<CryptoKey>;\n\n constructor(key: Uint8Array) {\n if (key.length !== 32) throw new Error('TokenCipher: key must be 32 bytes');\n this.key = webcrypto.subtle.importKey('raw', key as unknown as ArrayBuffer, 'AES-GCM', false, ['encrypt', 'decrypt']);\n }\n\n async encrypt(plaintext: string): Promise<string> {\n const key = await this.key;\n const nonce = webcrypto.getRandomValues(new Uint8Array(NONCE_LEN));\n const ct = new Uint8Array(await webcrypto.subtle.encrypt({ name: 'AES-GCM', iv: nonce }, key, new TextEncoder().encode(plaintext)));\n const combined = new Uint8Array(NONCE_LEN + ct.length);\n combined.set(nonce, 0);\n combined.set(ct, NONCE_LEN);\n return Buffer.from(combined).toString('base64');\n }\n\n async decrypt(encoded: string): Promise<string> {\n const bytes = new Uint8Array(Buffer.from(encoded, 'base64'));\n if (bytes.length <= NONCE_LEN) throw new Error('ciphertext too short');\n const nonce = bytes.slice(0, NONCE_LEN);\n const ct = bytes.slice(NONCE_LEN);\n const key = await this.key;\n const pt = new Uint8Array(await webcrypto.subtle.decrypt({ name: 'AES-GCM', iv: nonce }, key, ct as unknown as ArrayBuffer));\n return new TextDecoder().decode(pt);\n }\n}\n\nexport function generateKey(): Uint8Array {\n return webcrypto.getRandomValues(new Uint8Array(32));\n}\n","import { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nimport { generateKey } from './cipher';\nimport { TokenKeyring } from './keyring';\n\nexport interface KeyStoreOptions {\n keyring: TokenKeyring;\n /** Optional debug fallback path. When set, the key is mirrored to disk so\n * unsigned dev rebuilds can still decrypt previously persisted data. */\n debugFilePath?: string;\n}\n\n/** Loads or generates a 32-byte AES key, persisted in the OS keychain. */\nexport class KeyStore {\n constructor(private readonly opts: KeyStoreOptions) {}\n\n async loadOrCreate(): Promise<Uint8Array> {\n if (this.opts.debugFilePath) {\n try {\n const encoded = (await readFile(this.opts.debugFilePath, 'utf8')).trim();\n return decodeKey(encoded);\n } catch { /* fall through */ }\n }\n\n const encoded = await this.opts.keyring.get().catch(() => null);\n if (encoded) {\n const key = decodeKey(encoded);\n await this.writeDebugFile(encoded);\n return key;\n }\n\n const key = generateKey();\n const encodedNew = Buffer.from(key).toString('base64');\n await this.opts.keyring.set(encodedNew).catch(() => undefined);\n await this.writeDebugFile(encodedNew);\n return key;\n }\n\n private async writeDebugFile(encoded: string): Promise<void> {\n if (!this.opts.debugFilePath) return;\n await mkdir(dirname(this.opts.debugFilePath), { recursive: true });\n await writeFile(this.opts.debugFilePath, encoded, 'utf8');\n }\n}\n\nfunction decodeKey(encoded: string): Uint8Array {\n const bytes = new Uint8Array(Buffer.from(encoded, 'base64'));\n if (bytes.length !== 32) throw new Error('key has unexpected length');\n return bytes;\n}\n","import { createHash } from 'node:crypto';\nimport { hostname, platform } from 'node:os';\n\nexport interface DeviceFingerprint {\n fingerprint: string;\n platform: string;\n app_version: string;\n}\n\nasync function machineId(): Promise<string> {\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n try {\n const mod = (await dyn('node-machine-id').catch(() => null)) as { machineIdSync?: (orig?: boolean) => string } | null;\n if (mod?.machineIdSync) return mod.machineIdSync(true);\n } catch {\n // fallthrough\n }\n return hostname();\n}\n\nexport async function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint> {\n const id = await machineId();\n const plat = platform();\n const hash = createHash('sha256');\n hash.update(id);\n hash.update('::');\n hash.update(plat);\n hash.update('::');\n hash.update(appVersion);\n return {\n fingerprint: hash.digest('hex'),\n platform: plat,\n app_version: appVersion,\n };\n}\n","/**\n * Thin wrapper around `keytar` for desktop apps. Consumers install `keytar` as\n * an optional dependency; this module dynamically imports it so server-side\n * bundles never see it.\n */\nexport interface TokenKeyringOptions {\n service: string;\n account: string;\n}\n\ntype Keytar = {\n getPassword: (service: string, account: string) => Promise<string | null>;\n setPassword: (service: string, account: string, password: string) => Promise<void>;\n deletePassword: (service: string, account: string) => Promise<boolean>;\n};\n\nasync function loadKeytar(): Promise<Keytar> {\n // Use Function constructor so tsup/typescript don't try to resolve the\n // optional module at build time. Consumers add `keytar` as a peer/optional\n // dependency.\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n const mod = (await dyn('keytar').catch(() => null)) as { default?: Keytar } | null;\n if (!mod) throw new Error('TokenKeyring: install `keytar` to use the desktop keyring helper.');\n return mod.default ?? (mod as unknown as Keytar);\n}\n\nexport class TokenKeyring {\n private readonly service: string;\n private readonly account: string;\n\n constructor(opts: TokenKeyringOptions) {\n this.service = opts.service;\n this.account = opts.account;\n }\n\n async get(): Promise<string | null> {\n const k = await loadKeytar();\n return k.getPassword(this.service, this.account);\n }\n\n async set(value: string): Promise<void> {\n const k = await loadKeytar();\n await k.setPassword(this.service, this.account, value);\n }\n\n async delete(): Promise<void> {\n const k = await loadKeytar();\n await k.deletePassword(this.service, this.account);\n }\n}\n","import { BillingClient } from '../client';\nimport { TokenKeyring } from './keyring';\n\nexport class SessionStore {\n constructor(private readonly keyring: TokenKeyring) {}\n\n async hydrate(client: BillingClient): Promise<boolean> {\n const token = await this.keyring.get();\n if (!token) return false;\n client.setCustomerToken(token);\n return true;\n }\n\n async persist(client: BillingClient, token: string): Promise<void> {\n await this.keyring.set(token);\n client.setCustomerToken(token);\n }\n\n async clear(client: BillingClient): Promise<void> {\n await this.keyring.delete();\n client.setCustomerToken('');\n }\n\n async hasToken(): Promise<boolean> {\n const v = await this.keyring.get();\n return !!v;\n }\n}\n","import type { BillingClient } from '../client';\nimport type { Customer, OauthExchangeResponse } from '../client-types';\nimport type { LoopbackOutcome } from '../loopback';\nimport type { SessionStore } from './session';\n\nexport type AuthStatusState =\n | { state: 'loading' }\n | { state: 'guest' }\n | { state: 'authenticated'; customer: Customer; licensed: boolean };\n\nexport interface AuthControllerOptions {\n client: BillingClient;\n session: SessionStore;\n product: string;\n feature?: string;\n /** Loopback flow used by `oauthLogin`. */\n loopbackLogin: (provider: string) => Promise<LoopbackOutcome>;\n /** Override the runtime license check (e.g. cache, custom feature key). */\n checkLicense?: (client: BillingClient) => Promise<boolean>;\n}\n\nexport class AuthController {\n private listeners = new Set<(s: AuthStatusState) => void>();\n private current: AuthStatusState = { state: 'loading' };\n\n constructor(private readonly opts: AuthControllerOptions) {}\n\n subscribe(listener: (s: AuthStatusState) => void): () => void {\n this.listeners.add(listener);\n listener(this.current);\n return () => this.listeners.delete(listener);\n }\n\n snapshot(): AuthStatusState {\n return this.current;\n }\n\n private emit(next: AuthStatusState): void {\n this.current = next;\n this.listeners.forEach((l) => l(next));\n }\n\n async bootstrap(): Promise<void> {\n await this.opts.session.hydrate(this.opts.client);\n await this.refresh();\n }\n\n async refresh(): Promise<void> {\n if (!(await this.opts.session.hasToken())) {\n this.emit({ state: 'guest' });\n return;\n }\n try {\n const customer = await this.opts.client.customerMe();\n const licensed = this.opts.checkLicense\n ? await this.opts.checkLicense(this.opts.client)\n : await this.runDefaultLicenseCheck();\n this.emit({ state: 'authenticated', customer, licensed });\n } catch (e) {\n if ((e as { status?: number }).status === 401) {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n return;\n }\n throw e;\n }\n }\n\n private async runDefaultLicenseCheck(): Promise<boolean> {\n try {\n const resp = await this.opts.client.licenseCheck({\n product: this.opts.product,\n feature: this.opts.feature ?? 'general',\n });\n return resp.allowed;\n } catch {\n return false;\n }\n }\n\n async requestOtp(email: string, deviceFp?: string): Promise<void> {\n await this.opts.client.requestOtp({ email, device_fp: deviceFp ?? '' });\n }\n\n async verifyOtp(email: string, code: string, deviceFp?: string): Promise<Customer> {\n const resp = await this.opts.client.verifyOtp({\n email,\n code,\n device_fp: deviceFp ?? '',\n });\n await this.opts.session.persist(this.opts.client, resp.access_token);\n await this.refresh();\n return (await this.opts.client.customerMe());\n }\n\n async oauthLogin(provider: string): Promise<OauthExchangeResponse> {\n const outcome = await this.opts.loopbackLogin(provider);\n await this.opts.session.persist(this.opts.client, outcome.exchange.access_token);\n await this.refresh();\n return outcome.exchange;\n }\n\n async logout(): Promise<void> {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n }\n}\n"]}
|
package/package.json
CHANGED