@touchque/node 1.3.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,84 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file. The
4
+ format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ## [1.3.0] — 2026-09-06
7
+
8
+ ### Changed
9
+ - `webauthn.deleteCredential()` return type corrected to `{ deleted: boolean }`
10
+ (the API returns `deleted`, not `success`).
11
+ - Doc: the WebAuthn resource header no longer says "there's no client-side SDK"
12
+ — the browser side is `@touchque/web`.
13
+
14
+ ### Note
15
+ - Version bumped to **1.3.0** to align the whole server SDK line (node / go /
16
+ php / python) now that Go/PHP/Python have caught up to full parity — WebAuthn
17
+ resource, `getUser`, and the `telemetryToken` login-response field.
18
+
19
+ ## [1.2.0] — 2026-08-27
20
+
21
+ First release published to npm as **`@touchque/node`**.
22
+
23
+ ### Changed
24
+ - **Reverted** the 1.1.0 rename: the package name is `@touchque/node` again
25
+ (never actually shipped to npm under either name). Every consumer, the
26
+ public docs, and the MCP server already reference `@touchque/node`; the
27
+ cross-language rename in 1.1.0 was applied to the manifests but never to a
28
+ single consumer. Product scoping for other TouchQue products (e.g.
29
+ Compliance) is expressed by the package *name* under the shared
30
+ `@touchque` scope, not by a separate scope.
31
+ - `repository.url` corrected to `https://github.com/Touchque/touchque-sdks`.
32
+
33
+ ### Added
34
+ - `tq.webauthn.primaryOptions()` / `tq.webauthn.primaryVerify()` — the
35
+ passwordless-primary passkey login flow (no password, no prior
36
+ `login.request`), gated on `TenantPolicy.passwordlessLoginEnabled`.
37
+ Returns `{ requiresStepUp }` when risk/policy demands a second factor.
38
+ - `discoverable?: boolean` on `tq.webauthn.registerOptions()` — registers a
39
+ resident credential with forced user verification and a stable WebAuthn
40
+ user handle (the shape a passwordless-primary login authenticates against).
41
+ - `tq.auth.getUser({ externalUsername })` — fetch a user's link status
42
+ (`{ used, deviceId }`), previously added but unreleased.
43
+ - `telemetryToken` on `LoginRequestResponse` — issued when the integration
44
+ has `behavioralBiometricsEnabled`, for the `@touchque/behavioral-widget`
45
+ browser embed, previously added but unreleased.
46
+ - `WebAuthn` resource test suite (`src/resources/WebAuthn.test.ts`); tests
47
+ for `Auth.getUser()` and the Express middleware.
48
+
49
+ ### Packaging
50
+ - `package.json` is now publish-shaped: `files: ["dist"]` (no `src`/config in
51
+ the tarball), a conditional `exports` map (ESM/CJS + per-format types),
52
+ `engines.node >= 18`, `sideEffects: false`, `publishConfig.access public`,
53
+ a `prepublishOnly` build+test guard, and `express` declared as an optional
54
+ peer dependency (it is type-only in the SDK).
55
+
56
+ ## [1.1.0] — 2026-08-22
57
+
58
+ ### Changed
59
+ - **Breaking:** package renamed from `@touchque/node` to
60
+ `@touchque-authenticator/node` to reflect that this SDK is scoped to the
61
+ TouchQue Authenticator (2FA/MFA) product specifically, not TouchQue as a
62
+ whole (TouchQue's other products, e.g. KVKK Compliance, have their own
63
+ separately-named SDKs).
64
+
65
+ ### Fixed
66
+ - README no longer documents `tq.actionTypes.*` / `tq.users.*` — these
67
+ resources never existed in the SDK; the corresponding unused
68
+ `ActionType`/`ActionTypeOptions` types and the unused `UnlinkSecretOptions`/
69
+ `UnlinkSecretResponse` import in `Auth.ts` were also removed.
70
+
71
+ ### Added
72
+ - First real automated test suite (`vitest`, 36 tests covering `HttpClient`
73
+ signing/error-handling, `Auth`, `Login`, `Webhook`, and error classes).
74
+ The previous `package.json` `test` script pointed at a nonexistent
75
+ `test.js` and never ran anything.
76
+ - `LICENSE` (MIT) file, matching the license already declared in
77
+ `package.json`.
78
+
79
+ ## [1.0.1] — prior to this changelog
80
+
81
+ Initial public functionality: `Auth` (generateSecret/resetSecret/
82
+ validateSecret), `Login` (request/status/waitForApproval/verify/
83
+ approveWithRecoveryCode), `Webhook` (verify/isValid), Express middleware,
84
+ full TypeScript types.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TouchQue
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/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # @touchque/node
2
+
3
+ The official Node.js SDK for [TouchQue Authenticator](https://touchque.com) — add biometric 2FA to any application in minutes.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@touchque/node.svg)](https://www.npmjs.com/package/@touchque/node)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @touchque/node
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```typescript
18
+ import { TouchQue } from '@touchque/node';
19
+
20
+ const tq = new TouchQue({
21
+ apiKey: 'tq_auth_your_key', // From TouchQue Dashboard
22
+ apiSecret: 'your_api_secret', // From TouchQue Dashboard
23
+ });
24
+
25
+ // Protect any action with biometric 2FA — one line!
26
+ await tq.login.verify({
27
+ externalUsername: 'user@company.com',
28
+ type: 'LOGIN',
29
+ });
30
+ // If we reach here, the user approved on their phone ✅
31
+ ```
32
+
33
+ ## Features
34
+
35
+ - 🔐 **Biometric 2FA** — Face ID, Touch ID, fingerprint verification
36
+ - ⚡ **One-line integration** — `tq.login.verify()` does everything
37
+ - 🛡️ **HMAC-SHA256 signed requests** — every API call is cryptographically authenticated
38
+ - 📱 **Real-time push notifications** — instant approval requests to user devices
39
+ - 🔗 **Express.js middleware** — protect routes with a single middleware function
40
+ - 🪝 **Webhook verification** — validate incoming webhook signatures
41
+ - 📝 **Full TypeScript support** — complete type definitions included
42
+
43
+ ## Core Concepts
44
+
45
+ ### 1. Generate a Secret (User Onboarding)
46
+
47
+ When a user enables 2FA, generate a secret and show them a QR code:
48
+
49
+ ```typescript
50
+ const { secret, expiresAt } = await tq.auth.generateSecret({
51
+ externalUsername: 'user@company.com',
52
+ });
53
+
54
+ // Display `secret` as a QR code in your UI
55
+ // User scans it with the TouchQue mobile app
56
+ // Secret expires in 60 seconds if not scanned
57
+ ```
58
+
59
+ ### 2. Verify an Action (The Core Flow)
60
+
61
+ Protect any sensitive action with biometric verification:
62
+
63
+ ```typescript
64
+ import { TouchQueRejectedError, TouchQueTimeoutError } from '@touchque/node';
65
+
66
+ try {
67
+ await tq.login.verify({
68
+ externalUsername: 'user@company.com',
69
+ type: 'WITHDRAW', // Your custom action type
70
+ referenceId: 'txn_abc123', // Optional: your internal reference
71
+ timeout: 30000, // Optional: 30s timeout (default)
72
+ });
73
+
74
+ // ✅ User approved — execute the action
75
+ executeWithdrawal();
76
+
77
+ } catch (err) {
78
+ if (err instanceof TouchQueRejectedError) {
79
+ // ❌ User rejected on their device
80
+ } else if (err instanceof TouchQueTimeoutError) {
81
+ // ⏰ User didn't respond in time
82
+ }
83
+ }
84
+ ```
85
+
86
+ ### 3. Express.js Middleware
87
+
88
+ Protect any Express route with a single middleware:
89
+
90
+ ```typescript
91
+ import { TouchQue, requireTouchQue } from '@touchque/node';
92
+
93
+ const tq = new TouchQue({
94
+ apiKey: 'tq_auth_xxx',
95
+ apiSecret: 'your_secret',
96
+ });
97
+
98
+ // Protect a route — that's it!
99
+ app.post('/api/transfer',
100
+ authenticate, // Your auth middleware
101
+ requireTouchQue(tq, 'BANK_TRANSFER'), // TouchQue 2FA
102
+ (req, res) => {
103
+ // Only reaches here if user approved ✅
104
+ res.json({ success: true });
105
+ }
106
+ );
107
+ ```
108
+
109
+ ### 4. Webhook Verification
110
+
111
+ Verify that incoming webhooks are genuinely from TouchQue:
112
+
113
+ ```typescript
114
+ app.post('/webhooks/touchque',
115
+ express.raw({ type: 'application/json' }),
116
+ (req, res) => {
117
+ try {
118
+ const event = tq.webhook.verify({
119
+ rawBody: req.body.toString(),
120
+ signature: req.headers['x-tq-signature'] as string,
121
+ });
122
+
123
+ switch (event.event) {
124
+ case 'login.confirmed':
125
+ handleApproval(event);
126
+ break;
127
+ case 'login.rejected':
128
+ handleRejection(event);
129
+ break;
130
+ }
131
+
132
+ res.sendStatus(200);
133
+ } catch (err) {
134
+ // Invalid signature — not from TouchQue!
135
+ res.sendStatus(403);
136
+ }
137
+ }
138
+ );
139
+ ```
140
+
141
+ ## API Reference
142
+
143
+ ### `new TouchQue(config)`
144
+
145
+ | Parameter | Type | Required | Description |
146
+ |-----------|------|----------|-------------|
147
+ | `apiKey` | `string` | ✅ | Your API key (starts with `tq_auth_`) |
148
+ | `apiSecret` | `string` | ✅ | Your API secret |
149
+ | `baseUrl` | `string` | — | API base URL (default: `https://api-authenticator.touchque.com`) |
150
+ | `timeout` | `number` | — | Request timeout in ms (default: `10000`) |
151
+
152
+ ### `tq.auth`
153
+
154
+ | Method | Description |
155
+ |--------|-------------|
156
+ | `.generateSecret({ externalUsername })` | Generate a setup secret for user onboarding |
157
+ | `.resetSecret({ externalUsername })` | Reset (regenerate) a user's secret |
158
+ | `.validateSecret({ secret })` | Validate a setup secret code |
159
+ | `.getUser({ externalUsername })` | Link status (`used`, `deviceId`, …) without sending a push; throws 404 if unknown |
160
+
161
+ ### `tq.login`
162
+
163
+ | Method | Description |
164
+ |--------|-------------|
165
+ | `.request({ externalUsername, type, referenceId? })` | Send a 2FA request to the user's device |
166
+ | `.status(requestId)` | Check the status of a login request |
167
+ | `.waitForApproval({ requestId, timeout?, pollInterval? })` | Poll until approved/rejected/expired |
168
+ | `.verify({ externalUsername, type, ...options })` | **Recommended**: Request + wait in one call |
169
+ | `.approveWithRecoveryCode({ requestId, code })` | Approve a pending request using a backup recovery code |
170
+
171
+ The `request()` / `verify()` response carries `telemetryToken` when the
172
+ integration has behavioral biometrics enabled — pass it plus `requestId` to your
173
+ frontend for the [`@touchque/web`](https://www.npmjs.com/package/@touchque/web)
174
+ behavioral widget (`tq.behavioral.attach(...)`). There is no widget in this
175
+ server SDK.
176
+
177
+ ### `tq.webauthn` — passkey / FIDO2
178
+
179
+ Phishing-resistant login, alongside the push flow. Server-to-server: the passkey
180
+ ceremony runs in the browser (`@touchque/web`, or `navigator.credentials.*`);
181
+ your backend relays the JSON through these.
182
+
183
+ | Method | Description |
184
+ |--------|-------------|
185
+ | `.registerOptions({ externalUsername, discoverable? })` / `.registerVerify({ externalUsername, response, label? })` | Register a passkey |
186
+ | `.authenticateOptions({ requestId })` / `.authenticateVerify({ requestId, response })` | Approve a pending `login.request` with a passkey |
187
+ | `.primaryOptions({ externalUsername })` / `.primaryVerify({ attemptId, response })` | Passwordless-primary login |
188
+ | `.listCredentials({ externalUsername })` / `.deleteCredential(id)` | Credential management |
189
+
190
+ ### `tq.webhook`
191
+
192
+ | Method | Description |
193
+ |--------|-------------|
194
+ | `.verify({ rawBody, signature?, toleranceSeconds? })` | Verify webhook signature (throws on invalid) |
195
+ | `.isValid({ rawBody, signature? })` | Check webhook signature (returns boolean) |
196
+
197
+ ### Express Middleware
198
+
199
+ ```typescript
200
+ requireTouchQue(tqClient, actionType, options?)
201
+ ```
202
+
203
+ | Option | Type | Description |
204
+ |--------|------|-------------|
205
+ | `timeout` | `number` | Approval timeout in ms (default: `30000`) |
206
+ | `getUserId` | `(req) => string` | Custom function to extract user ID from request |
207
+ | `getReferenceId` | `(req) => string` | Custom function to extract reference ID |
208
+
209
+ ## Error Handling
210
+
211
+ All SDK errors extend `TouchQueError`:
212
+
213
+ | Error Class | When Thrown |
214
+ |-------------|-----------|
215
+ | `TouchQueAPIError` | API returned a non-2xx response |
216
+ | `TouchQueRejectedError` | User rejected the 2FA request |
217
+ | `TouchQueTimeoutError` | User didn't respond in time |
218
+ | `TouchQueWebhookSignatureError` | Webhook signature verification failed |
219
+ | `TouchQueConfigError` | Invalid SDK configuration |
220
+
221
+ ```typescript
222
+ import {
223
+ TouchQueAPIError,
224
+ TouchQueRejectedError,
225
+ TouchQueTimeoutError,
226
+ } from '@touchque/node';
227
+
228
+ try {
229
+ await tq.login.verify({ ... });
230
+ } catch (err) {
231
+ if (err instanceof TouchQueRejectedError) {
232
+ console.log('Request ID:', err.requestId);
233
+ }
234
+ if (err instanceof TouchQueAPIError) {
235
+ console.log('HTTP Status:', err.status);
236
+ console.log('Error Data:', err.data);
237
+ }
238
+ }
239
+ ```
240
+
241
+ ## Security
242
+
243
+ - All requests are signed with **HMAC-SHA256** using your API secret
244
+ - Signatures include method, path, timestamp, nonce, and body hash
245
+ - Webhook verification uses **timing-safe comparison** to prevent timing attacks
246
+ - Your API secret never leaves your server
247
+
248
+ ## Requirements
249
+
250
+ - Node.js 18+
251
+ - An active [TouchQue Dashboard](https://authenticator.touchque.com) account
252
+
253
+ ## License
254
+
255
+ MIT © [TouchQue](https://touchque.com)