kavachos 0.0.2 → 0.0.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/agent/index.d.ts +4 -5
- package/dist/agent/index.js +2 -2
- package/dist/audit/index.d.ts +3 -4
- package/dist/audit/index.js +2 -2
- package/dist/auth/index.d.ts +1719 -2
- package/dist/auth/index.js +2 -1
- package/dist/{chunk-I4J4KKKK.js → chunk-5DT4DN4Y.js} +9 -3
- package/dist/chunk-5DT4DN4Y.js.map +1 -0
- package/dist/chunk-KL6XW4S4.js +10774 -0
- package/dist/chunk-KL6XW4S4.js.map +1 -0
- package/dist/{chunk-DEVV32BE.js → chunk-OVGNZ5OX.js} +3 -3
- package/dist/{chunk-DEVV32BE.js.map → chunk-OVGNZ5OX.js.map} +1 -1
- package/dist/{chunk-N7VZO6SP.js → chunk-SJGSPIAD.js} +3 -3
- package/dist/{chunk-N7VZO6SP.js.map → chunk-SJGSPIAD.js.map} +1 -1
- package/dist/chunk-V66UUIA7.js +480 -0
- package/dist/chunk-V66UUIA7.js.map +1 -0
- package/dist/index.d.ts +1125 -14
- package/dist/index.js +2986 -111
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/permission/index.d.ts +4 -5
- package/dist/permission/index.js +2 -2
- package/dist/types-Xk83hv4O.d.ts +7759 -0
- package/dist/{types-B4sQA44H.d.ts → types-mwupB57A.d.ts} +5 -5
- package/package.json +1 -1
- package/dist/chunk-7RKVTHFC.js +0 -96
- package/dist/chunk-7RKVTHFC.js.map +0 -1
- package/dist/chunk-I4J4KKKK.js.map +0 -1
- package/dist/chunk-UEE7OYLG.js +0 -161
- package/dist/chunk-UEE7OYLG.js.map +0 -1
- package/dist/types-WP-mKSdQ.d.ts +0 -2349
- package/dist/types-_7hIICee.d.ts +0 -52
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { Z as AuthAdapter, o as ResolvedUser, F as KavachPlugin, D as Database, I as AdminConfig, p as SessionManager, Q as ApiKeyManagerConfig, a1 as EmailOtpConfig, a4 as MagicLinkConfig, a7 as OrgConfig, ac as PasskeyConfig, z as PluginEndpoint, aq as TotpConfig } from '../types-Xk83hv4O.js';
|
|
3
|
+
export { u as AdminModule, J as AdminUser, N as ApiKey, v as ApiKeyManagerModule, _ as CaptchaConfig, y as CaptchaModule, $ as CaptchaVerifyResult, E as EmailOtpModule, r as MagicLinkModule, a6 as OidcProvider, a8 as OrgInvitation, a9 as OrgMember, O as OrgModule, aa as OrgRole, ab as Organization, ad as PasskeyCredential, s as PasskeyModule, af as PhoneAuthConfig, x as PhoneAuthModule, ai as SSO_ERROR, aj as SamlProvider, al as SsoAuditEvent, am as SsoConfig, an as SsoConnection, ao as SsoError, t as SsoModule, T as TotpModule, ar as TotpSetup, as as UsernameAuthConfig, w as UsernameAuthModule, ba as WebhookConfig, bb as WebhookEvent, W as WebhookModule, aC as createAdminModule, aD as createApiKeyManagerModule, aF as createCaptchaModule, aI as createEmailOtpModule, aJ as createMagicLinkModule, aK as createOrgModule, aL as createPasskeyModule, aM as createPhoneAuthModule, aO as createSsoModule, aP as createTotpModule, aQ as createUsernameAuthModule, bc as createWebhookModule } from '../types-Xk83hv4O.js';
|
|
4
|
+
import { R as Result } from '../types-mwupB57A.js';
|
|
5
|
+
import * as jose from 'jose';
|
|
6
|
+
import 'drizzle-orm/better-sqlite3';
|
|
7
|
+
import 'drizzle-orm/sqlite-core';
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
10
|
* JWT bearer-token auth adapter.
|
|
@@ -140,4 +145,1716 @@ interface HeaderAuthOptions {
|
|
|
140
145
|
*/
|
|
141
146
|
declare function headerAuth(options?: HeaderAuthOptions): AuthAdapter;
|
|
142
147
|
|
|
143
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Additional user/session fields plugin for KavachOS.
|
|
150
|
+
*
|
|
151
|
+
* Lets callers extend the user and session schemas with typed custom fields
|
|
152
|
+
* without writing any database migrations. Fields are stored in the existing
|
|
153
|
+
* `user.metadata` or `session.metadata` JSON columns.
|
|
154
|
+
*
|
|
155
|
+
* Field types: `string`, `number`, `boolean`, `json`.
|
|
156
|
+
* Required fields must be present when calling `setUserFields`.
|
|
157
|
+
* Fields not in the schema are rejected during `validate()`.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```typescript
|
|
161
|
+
* import { createKavach } from 'kavachos';
|
|
162
|
+
* import { additionalFields } from 'kavachos/auth';
|
|
163
|
+
*
|
|
164
|
+
* const kavach = await createKavach({
|
|
165
|
+
* database: { provider: 'sqlite', url: 'kavach.db' },
|
|
166
|
+
* plugins: [
|
|
167
|
+
* additionalFields({
|
|
168
|
+
* user: {
|
|
169
|
+
* plan: { type: 'string', required: false, defaultValue: 'free' },
|
|
170
|
+
* credits: { type: 'number', required: false, defaultValue: 0 },
|
|
171
|
+
* },
|
|
172
|
+
* }),
|
|
173
|
+
* ],
|
|
174
|
+
* });
|
|
175
|
+
*
|
|
176
|
+
* const mod = kavach.plugins.getContext().additionalFields as AdditionalFieldsModule;
|
|
177
|
+
* await mod.setUserFields(userId, { plan: 'pro', credits: 100 });
|
|
178
|
+
* const fields = await mod.getUserFields(userId);
|
|
179
|
+
* // => { plan: 'pro', credits: 100 }
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
|
|
183
|
+
interface FieldDefinition {
|
|
184
|
+
type: "string" | "number" | "boolean" | "json";
|
|
185
|
+
required?: boolean;
|
|
186
|
+
defaultValue?: unknown;
|
|
187
|
+
}
|
|
188
|
+
interface AdditionalFieldsConfig {
|
|
189
|
+
/** Custom fields for users (stored in user.metadata). */
|
|
190
|
+
user?: Record<string, FieldDefinition>;
|
|
191
|
+
/** Custom fields for sessions (stored in session.metadata). */
|
|
192
|
+
session?: Record<string, FieldDefinition>;
|
|
193
|
+
}
|
|
194
|
+
interface ValidationResult {
|
|
195
|
+
valid: boolean;
|
|
196
|
+
errors?: string[];
|
|
197
|
+
}
|
|
198
|
+
interface AdditionalFieldsModule {
|
|
199
|
+
/**
|
|
200
|
+
* Return the additional fields stored in `user.metadata` for the given user.
|
|
201
|
+
*
|
|
202
|
+
* Fields missing from the stored metadata are filled with their `defaultValue`
|
|
203
|
+
* (if one is defined in the schema).
|
|
204
|
+
*/
|
|
205
|
+
getUserFields(userId: string): Promise<Record<string, unknown>>;
|
|
206
|
+
/**
|
|
207
|
+
* Write `fields` into `user.metadata`, merging with any already-stored values.
|
|
208
|
+
*
|
|
209
|
+
* Validates against the `user` schema before writing.
|
|
210
|
+
* Throws when the user does not exist or validation fails.
|
|
211
|
+
*/
|
|
212
|
+
setUserFields(userId: string, fields: Record<string, unknown>): Promise<void>;
|
|
213
|
+
/**
|
|
214
|
+
* Return the additional fields stored in `session.metadata` for the given session.
|
|
215
|
+
*/
|
|
216
|
+
getSessionFields(sessionId: string): Promise<Record<string, unknown>>;
|
|
217
|
+
/**
|
|
218
|
+
* Write `fields` into `session.metadata`, merging with any already-stored values.
|
|
219
|
+
*
|
|
220
|
+
* Validates against the `session` schema before writing.
|
|
221
|
+
* Throws when the session does not exist or validation fails.
|
|
222
|
+
*/
|
|
223
|
+
setSessionFields(sessionId: string, fields: Record<string, unknown>): Promise<void>;
|
|
224
|
+
/**
|
|
225
|
+
* Validate a field map against the "user" or "session" schema.
|
|
226
|
+
*
|
|
227
|
+
* Returns `{ valid: true }` on success or `{ valid: false, errors: [...] }` on
|
|
228
|
+
* failure. Does not throw.
|
|
229
|
+
*/
|
|
230
|
+
validate(fields: Record<string, unknown>, schema: "user" | "session"): ValidationResult;
|
|
231
|
+
}
|
|
232
|
+
declare function createAdditionalFieldsModule(config: AdditionalFieldsConfig, db: Database): AdditionalFieldsModule;
|
|
233
|
+
declare function additionalFields(config?: AdditionalFieldsConfig): KavachPlugin;
|
|
234
|
+
|
|
235
|
+
declare function admin(config?: AdminConfig): KavachPlugin;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Anonymous authentication for KavachOS.
|
|
239
|
+
*
|
|
240
|
+
* Lets users start as guests without providing credentials. The anonymous
|
|
241
|
+
* user can later be upgraded to a real account by supplying an email.
|
|
242
|
+
*
|
|
243
|
+
* Anonymous users are stored in `kavach_users` with a synthetic placeholder
|
|
244
|
+
* email (`anon_<uuid>@kavachos.anonymous`) and a metadata flag
|
|
245
|
+
* `{ anonymous: true }`. This satisfies the NOT NULL UNIQUE constraint on
|
|
246
|
+
* the email column while keeping them easily identifiable.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* const anon = createAnonymousAuthModule(config, db, sessionManager);
|
|
251
|
+
*
|
|
252
|
+
* // On first visit
|
|
253
|
+
* const { userId, sessionToken } = await anon.createAnonymousUser();
|
|
254
|
+
*
|
|
255
|
+
* // Later, when user signs up
|
|
256
|
+
* await anon.upgradeUser(userId, { email: 'alice@example.com', name: 'Alice' });
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
|
|
260
|
+
interface AnonymousAuthConfig {
|
|
261
|
+
/** How long anonymous sessions last in seconds (default: 86400 = 24 hours) */
|
|
262
|
+
sessionTtlSeconds?: number;
|
|
263
|
+
/** Whether to allow anonymous users to create agents (default: false) */
|
|
264
|
+
allowAgentCreation?: boolean;
|
|
265
|
+
}
|
|
266
|
+
interface AnonymousAuthModule {
|
|
267
|
+
/** Create an anonymous user and a session. */
|
|
268
|
+
createAnonymousUser(): Promise<{
|
|
269
|
+
userId: string;
|
|
270
|
+
sessionToken: string;
|
|
271
|
+
}>;
|
|
272
|
+
/** Upgrade an anonymous user to a real account by setting their email. */
|
|
273
|
+
upgradeUser(anonymousUserId: string, upgrade: {
|
|
274
|
+
email: string;
|
|
275
|
+
name?: string;
|
|
276
|
+
}): Promise<void>;
|
|
277
|
+
/** Check if a user was created as anonymous and has not been upgraded. */
|
|
278
|
+
isAnonymous(userId: string): Promise<boolean>;
|
|
279
|
+
/**
|
|
280
|
+
* Delete anonymous users older than `maxAgeMs` and their sessions.
|
|
281
|
+
* Returns the number of users removed.
|
|
282
|
+
*/
|
|
283
|
+
cleanup(maxAgeMs?: number): Promise<number>;
|
|
284
|
+
}
|
|
285
|
+
declare function createAnonymousAuthModule(config: AnonymousAuthConfig, db: Database, sessionManager: SessionManager): AnonymousAuthModule;
|
|
286
|
+
|
|
287
|
+
declare function anonymousAuth(config?: AnonymousAuthConfig): KavachPlugin;
|
|
288
|
+
|
|
289
|
+
declare function apiKeys(config?: ApiKeyManagerConfig): KavachPlugin;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Custom session fields plugin for KavachOS.
|
|
293
|
+
*
|
|
294
|
+
* Lets callers attach arbitrary data to sessions at creation time and read or
|
|
295
|
+
* update that data later. Everything is stored in the existing
|
|
296
|
+
* `session.metadata.custom` sub-key — no new database columns are required.
|
|
297
|
+
*
|
|
298
|
+
* Two integration points:
|
|
299
|
+
*
|
|
300
|
+
* 1. `defaultFields` — merged into every new session automatically.
|
|
301
|
+
* 2. `onSessionCreate` — async callback that receives the userId (and
|
|
302
|
+
* optionally the originating Request) and returns additional fields to
|
|
303
|
+
* merge. Runs once per session, during the plugin's `onSessionCreate` hook.
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```typescript
|
|
307
|
+
* import { createKavach } from 'kavachos';
|
|
308
|
+
* import { customSession } from 'kavachos/auth';
|
|
309
|
+
*
|
|
310
|
+
* const kavach = await createKavach({
|
|
311
|
+
* database: { provider: 'sqlite', url: 'kavach.db' },
|
|
312
|
+
* auth: { session: { secret: process.env.SESSION_SECRET } },
|
|
313
|
+
* plugins: [
|
|
314
|
+
* customSession({
|
|
315
|
+
* defaultFields: { theme: 'dark' },
|
|
316
|
+
* onSessionCreate: async (userId) => ({ lastSeen: Date.now() }),
|
|
317
|
+
* }),
|
|
318
|
+
* ],
|
|
319
|
+
* });
|
|
320
|
+
*
|
|
321
|
+
* // After a session is created via kavach.auth.session.create(...)
|
|
322
|
+
* const mod = kavach.plugins.getContext().customSession as CustomSessionModule;
|
|
323
|
+
* const fields = await mod.getSessionFields(session.id);
|
|
324
|
+
* // => { theme: 'dark', lastSeen: 1234567890 }
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
|
|
328
|
+
interface CustomSessionConfig {
|
|
329
|
+
/** Fields merged into every new session's metadata.custom on creation. */
|
|
330
|
+
defaultFields?: Record<string, unknown>;
|
|
331
|
+
/**
|
|
332
|
+
* Hook called when a new session is being created.
|
|
333
|
+
*
|
|
334
|
+
* The return value is merged into `session.metadata.custom` alongside any
|
|
335
|
+
* `defaultFields`. If both define the same key, the hook's value wins.
|
|
336
|
+
*/
|
|
337
|
+
onSessionCreate?: (userId: string, request?: Request) => Promise<Record<string, unknown>>;
|
|
338
|
+
}
|
|
339
|
+
interface CustomSessionModule {
|
|
340
|
+
/**
|
|
341
|
+
* Return the custom fields stored in `session.metadata.custom`.
|
|
342
|
+
*
|
|
343
|
+
* Returns `null` when the session does not exist or has no custom data.
|
|
344
|
+
*/
|
|
345
|
+
getSessionFields(sessionId: string): Promise<Record<string, unknown> | null>;
|
|
346
|
+
/**
|
|
347
|
+
* Merge `fields` into `session.metadata.custom`, overwriting any keys that
|
|
348
|
+
* already exist. Existing keys not present in `fields` are preserved.
|
|
349
|
+
*/
|
|
350
|
+
updateSessionFields(sessionId: string, fields: Record<string, unknown>): Promise<void>;
|
|
351
|
+
}
|
|
352
|
+
declare function createCustomSessionModule(_config: CustomSessionConfig, db: Database): CustomSessionModule;
|
|
353
|
+
declare function customSession(config?: CustomSessionConfig): KavachPlugin;
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* OAuth Device Authorization Grant (RFC 8628) for KavachOS.
|
|
357
|
+
*
|
|
358
|
+
* Supports TVs, CLI tools, smart displays, and any device where the user
|
|
359
|
+
* cannot easily type a URL or complete an interactive login flow. The device
|
|
360
|
+
* requests a short code, the user approves on a secondary device (phone /
|
|
361
|
+
* browser), and the original device polls until authorization is granted.
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```typescript
|
|
365
|
+
* const deviceAuth = createDeviceAuthModule({
|
|
366
|
+
* verificationUri: 'https://example.com/device',
|
|
367
|
+
* });
|
|
368
|
+
*
|
|
369
|
+
* // 1. CLI tool requests codes
|
|
370
|
+
* const { userCode, verificationUri } = await deviceAuth.requestCode();
|
|
371
|
+
* console.log(`Visit ${verificationUri} and enter: ${userCode}`);
|
|
372
|
+
*
|
|
373
|
+
* // 2. Poll from CLI
|
|
374
|
+
* const status = await deviceAuth.checkAuthorization(deviceCode);
|
|
375
|
+
*
|
|
376
|
+
* // 3. User approves on browser after logging in
|
|
377
|
+
* await deviceAuth.authorize(userCode, userId);
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
interface DeviceAuthConfig {
|
|
381
|
+
/** Code length for the human-readable user code segment (default: 4, produces "XXXX-XXXX") */
|
|
382
|
+
codeLength?: number;
|
|
383
|
+
/** Code expiry in seconds (default: 900 = 15 min) */
|
|
384
|
+
codeExpirySeconds?: number;
|
|
385
|
+
/** Polling interval in seconds (default: 5) */
|
|
386
|
+
pollIntervalSeconds?: number;
|
|
387
|
+
/** Verification URL shown to user */
|
|
388
|
+
verificationUri: string;
|
|
389
|
+
}
|
|
390
|
+
interface DeviceCodeResponse {
|
|
391
|
+
deviceCode: string;
|
|
392
|
+
userCode: string;
|
|
393
|
+
verificationUri: string;
|
|
394
|
+
verificationUriComplete: string;
|
|
395
|
+
expiresIn: number;
|
|
396
|
+
interval: number;
|
|
397
|
+
}
|
|
398
|
+
type DeviceAuthStatus = {
|
|
399
|
+
status: "pending";
|
|
400
|
+
} | {
|
|
401
|
+
status: "authorized";
|
|
402
|
+
userId: string;
|
|
403
|
+
} | {
|
|
404
|
+
status: "expired";
|
|
405
|
+
} | {
|
|
406
|
+
status: "denied";
|
|
407
|
+
};
|
|
408
|
+
interface DeviceAuthModule {
|
|
409
|
+
/** Start device auth flow: returns device_code, user_code, verification_uri */
|
|
410
|
+
requestCode(): Promise<DeviceCodeResponse>;
|
|
411
|
+
/** Check if user has authorized (called by polling device) */
|
|
412
|
+
checkAuthorization(deviceCode: string): Promise<DeviceAuthStatus>;
|
|
413
|
+
/** Authorize a device (called after user logs in on phone/browser) */
|
|
414
|
+
authorize(userCode: string, userId: string): Promise<void>;
|
|
415
|
+
/** Deny a device code (user explicitly rejects) */
|
|
416
|
+
deny(userCode: string): Promise<void>;
|
|
417
|
+
/** Handle HTTP requests for the device auth endpoints */
|
|
418
|
+
handleRequest(request: Request): Promise<Response | null>;
|
|
419
|
+
}
|
|
420
|
+
declare function createDeviceAuthModule(config: DeviceAuthConfig): DeviceAuthModule;
|
|
421
|
+
|
|
422
|
+
declare function deviceAuth(config: DeviceAuthConfig): KavachPlugin;
|
|
423
|
+
|
|
424
|
+
declare function emailOtp(config: EmailOtpConfig): KavachPlugin;
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* GDPR module for KavachOS.
|
|
428
|
+
*
|
|
429
|
+
* Implements Article 17 (right to erasure) and Article 20 (right to data
|
|
430
|
+
* portability) for user accounts. Compliance-critical: every data removal
|
|
431
|
+
* path is explicit about which tables are affected.
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* ```typescript
|
|
435
|
+
* const gdpr = createGdprModule(db);
|
|
436
|
+
*
|
|
437
|
+
* // Export all data for a user
|
|
438
|
+
* const export = await gdpr.exportUserData(userId);
|
|
439
|
+
*
|
|
440
|
+
* // Delete account, keeping anonymized audit trail
|
|
441
|
+
* const result = await gdpr.deleteUser(userId, { keepAuditLogs: true });
|
|
442
|
+
*
|
|
443
|
+
* // Anonymize PII but keep the account (e.g., for orgs that require it)
|
|
444
|
+
* await gdpr.anonymizeUser(userId);
|
|
445
|
+
* ```
|
|
446
|
+
*/
|
|
447
|
+
|
|
448
|
+
interface UserDataExport {
|
|
449
|
+
user: {
|
|
450
|
+
id: string;
|
|
451
|
+
email: string;
|
|
452
|
+
name: string | null;
|
|
453
|
+
createdAt: string;
|
|
454
|
+
};
|
|
455
|
+
agents: Array<{
|
|
456
|
+
id: string;
|
|
457
|
+
name: string;
|
|
458
|
+
type: string;
|
|
459
|
+
status: string;
|
|
460
|
+
createdAt: string;
|
|
461
|
+
}>;
|
|
462
|
+
sessions: Array<{
|
|
463
|
+
id: string;
|
|
464
|
+
createdAt: string;
|
|
465
|
+
expiresAt: string;
|
|
466
|
+
}>;
|
|
467
|
+
auditLogs: Array<{
|
|
468
|
+
action: string;
|
|
469
|
+
resource: string;
|
|
470
|
+
result: string;
|
|
471
|
+
timestamp: string;
|
|
472
|
+
}>;
|
|
473
|
+
delegations: Array<{
|
|
474
|
+
fromAgent: string;
|
|
475
|
+
toAgent: string;
|
|
476
|
+
createdAt: string;
|
|
477
|
+
}>;
|
|
478
|
+
organizations: Array<{
|
|
479
|
+
id: string;
|
|
480
|
+
name: string;
|
|
481
|
+
role: string;
|
|
482
|
+
}>;
|
|
483
|
+
apiKeys: Array<{
|
|
484
|
+
id: string;
|
|
485
|
+
name: string;
|
|
486
|
+
createdAt: string;
|
|
487
|
+
}>;
|
|
488
|
+
exportedAt: string;
|
|
489
|
+
}
|
|
490
|
+
interface DeleteOptions {
|
|
491
|
+
/**
|
|
492
|
+
* Keep anonymized audit logs (default: true).
|
|
493
|
+
*
|
|
494
|
+
* Required for most compliance frameworks — audit records must survive
|
|
495
|
+
* account deletion. User/agent identity is replaced with a stable hash so
|
|
496
|
+
* aggregate reporting remains consistent across deleted accounts.
|
|
497
|
+
*/
|
|
498
|
+
keepAuditLogs?: boolean;
|
|
499
|
+
/**
|
|
500
|
+
* Also delete organizations owned by this user (default: false).
|
|
501
|
+
*
|
|
502
|
+
* When false, ownership is left in place so other members are unaffected.
|
|
503
|
+
* Set to true only when the org has no other members or its data should
|
|
504
|
+
* also be erased.
|
|
505
|
+
*/
|
|
506
|
+
deleteOrganizations?: boolean;
|
|
507
|
+
}
|
|
508
|
+
interface DeleteResult {
|
|
509
|
+
deletedAgents: number;
|
|
510
|
+
deletedSessions: number;
|
|
511
|
+
deletedDelegations: number;
|
|
512
|
+
deletedApiKeys: number;
|
|
513
|
+
anonymizedAuditLogs: number;
|
|
514
|
+
}
|
|
515
|
+
interface GdprModule {
|
|
516
|
+
/** Export all user data as a structured JSON object (GDPR Article 20). */
|
|
517
|
+
exportUserData(userId: string): Promise<UserDataExport>;
|
|
518
|
+
/** Delete all user data (GDPR Article 17). Returns counts of removed records. */
|
|
519
|
+
deleteUser(userId: string, options?: DeleteOptions): Promise<DeleteResult>;
|
|
520
|
+
/**
|
|
521
|
+
* Anonymize user data instead of deleting.
|
|
522
|
+
*
|
|
523
|
+
* Replaces PII (email, name) with deterministic anonymous values while
|
|
524
|
+
* keeping the account structure intact. Useful when org membership or
|
|
525
|
+
* audit referential integrity must be preserved.
|
|
526
|
+
*/
|
|
527
|
+
anonymizeUser(userId: string): Promise<void>;
|
|
528
|
+
}
|
|
529
|
+
declare function createGdprModule(db: Database): GdprModule;
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* GDPR plugin for KavachOS.
|
|
533
|
+
*
|
|
534
|
+
* Exposes three self-service endpoints that authenticated users can call
|
|
535
|
+
* to exercise their data rights under GDPR Articles 17 and 20.
|
|
536
|
+
*
|
|
537
|
+
* Endpoints:
|
|
538
|
+
* GET /auth/gdpr/export – download a JSON export of all personal data
|
|
539
|
+
* DELETE /auth/gdpr/delete – permanently delete the account
|
|
540
|
+
* POST /auth/gdpr/anonymize – strip PII but keep the account shell
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```typescript
|
|
544
|
+
* import { createKavach } from 'kavachos';
|
|
545
|
+
* import { gdpr } from 'kavachos/auth';
|
|
546
|
+
*
|
|
547
|
+
* const kavach = await createKavach({
|
|
548
|
+
* database: { provider: 'sqlite', url: 'kavach.db' },
|
|
549
|
+
* plugins: [gdpr()],
|
|
550
|
+
* });
|
|
551
|
+
* ```
|
|
552
|
+
*/
|
|
553
|
+
|
|
554
|
+
declare function gdpr(): KavachPlugin;
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Have I Been Pwned password checking for KavachOS.
|
|
558
|
+
*
|
|
559
|
+
* Uses the k-anonymity model: only the first 5 hex characters of the SHA-1
|
|
560
|
+
* hash are sent to the API. The full hash (and the password itself) never
|
|
561
|
+
* leave the process.
|
|
562
|
+
*
|
|
563
|
+
* @see https://haveibeenpwned.com/API/v3#PwnedPasswords
|
|
564
|
+
*/
|
|
565
|
+
interface HibpConfig {
|
|
566
|
+
/** Reject passwords seen in more than N breaches (default: 0, reject any). */
|
|
567
|
+
threshold?: number;
|
|
568
|
+
/** Custom API base URL, e.g. for a self-hosted HIBP instance. */
|
|
569
|
+
apiUrl?: string;
|
|
570
|
+
/** Request timeout in milliseconds (default: 5000). */
|
|
571
|
+
timeoutMs?: number;
|
|
572
|
+
/**
|
|
573
|
+
* What to do when the HIBP API is unreachable or returns an error.
|
|
574
|
+
* - `'allow'` – treat the password as clean and let the user continue.
|
|
575
|
+
* - `'block'` – reject the password to be safe.
|
|
576
|
+
* Default: `'allow'`.
|
|
577
|
+
*/
|
|
578
|
+
onError?: "allow" | "block";
|
|
579
|
+
}
|
|
580
|
+
interface HibpModule {
|
|
581
|
+
/**
|
|
582
|
+
* Check whether the password appears in any known data breach.
|
|
583
|
+
* Returns the number of times it has been seen, or 0 if clean / API error
|
|
584
|
+
* with `onError: 'allow'`.
|
|
585
|
+
*/
|
|
586
|
+
check(password: string): Promise<number>;
|
|
587
|
+
/**
|
|
588
|
+
* Like `check`, but throws a `HibpBreachedError` when the breach count
|
|
589
|
+
* exceeds the configured threshold.
|
|
590
|
+
*/
|
|
591
|
+
enforce(password: string): Promise<void>;
|
|
592
|
+
}
|
|
593
|
+
declare class HibpBreachedError extends Error {
|
|
594
|
+
readonly count: number;
|
|
595
|
+
constructor(count: number);
|
|
596
|
+
}
|
|
597
|
+
declare function createHibpModule(config?: HibpConfig): HibpModule;
|
|
598
|
+
declare class HibpApiError extends Error {
|
|
599
|
+
constructor(message: string);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* JWT session plugin for KavachOS.
|
|
604
|
+
*
|
|
605
|
+
* Issues short-lived access JWTs and long-lived refresh tokens for general-purpose
|
|
606
|
+
* session management. This is distinct from the internal OIDC provider JWT — this
|
|
607
|
+
* plugin is for apps that want to vend their own session tokens without running a
|
|
608
|
+
* full OIDC flow.
|
|
609
|
+
*
|
|
610
|
+
* Access tokens are stateless JWTs (no DB lookup on verify). Refresh tokens are
|
|
611
|
+
* opaque random strings stored hashed in the database, soft-revoked on use.
|
|
612
|
+
*
|
|
613
|
+
* @example
|
|
614
|
+
* ```typescript
|
|
615
|
+
* const sessions = createJwtSessionModule({
|
|
616
|
+
* secret: process.env.SESSION_SECRET,
|
|
617
|
+
* issuer: 'https://myapp.com',
|
|
618
|
+
* customClaims: (user) => ({ role: user.role, org: user.orgId }),
|
|
619
|
+
* }, db);
|
|
620
|
+
*
|
|
621
|
+
* // On login
|
|
622
|
+
* const result = await sessions.createSession({ id: user.id, email: user.email });
|
|
623
|
+
* if (result.success) {
|
|
624
|
+
* const { accessToken, refreshToken, expiresIn } = result.data;
|
|
625
|
+
* }
|
|
626
|
+
*
|
|
627
|
+
* // On API request
|
|
628
|
+
* const verified = await sessions.verifySession(bearerToken);
|
|
629
|
+
* if (verified.success) {
|
|
630
|
+
* const { userId, claims } = verified.data;
|
|
631
|
+
* }
|
|
632
|
+
*
|
|
633
|
+
* // On token refresh
|
|
634
|
+
* const refreshed = await sessions.refreshSession(refreshToken);
|
|
635
|
+
*
|
|
636
|
+
* // On logout
|
|
637
|
+
* await sessions.revokeSession(refreshToken);
|
|
638
|
+
* ```
|
|
639
|
+
*/
|
|
640
|
+
|
|
641
|
+
interface JwtSessionConfig {
|
|
642
|
+
/** Signing key. A plain string uses HMAC-SHA256 (must be >= 32 chars).
|
|
643
|
+
* Pass a `CryptoKey` or `JsonWebKey` for RSA/EC algorithms. */
|
|
644
|
+
secret: string | CryptoKey | JsonWebKey;
|
|
645
|
+
/** JWT algorithm. Defaults to 'HS256' for string secrets, 'RS256' for keys. */
|
|
646
|
+
algorithm?: string;
|
|
647
|
+
/** Access token TTL in seconds. Default: 900 (15 min). */
|
|
648
|
+
accessTokenTtl?: number;
|
|
649
|
+
/** Refresh token TTL in seconds. Default: 604800 (7 days). */
|
|
650
|
+
refreshTokenTtl?: number;
|
|
651
|
+
/** JWT `iss` claim. */
|
|
652
|
+
issuer?: string;
|
|
653
|
+
/** JWT `aud` claim. */
|
|
654
|
+
audience?: string;
|
|
655
|
+
/** Attach extra claims to the access token payload. */
|
|
656
|
+
customClaims?: (user: {
|
|
657
|
+
id: string;
|
|
658
|
+
email?: string;
|
|
659
|
+
name?: string;
|
|
660
|
+
}) => Record<string, unknown>;
|
|
661
|
+
}
|
|
662
|
+
interface SessionUser {
|
|
663
|
+
id: string;
|
|
664
|
+
email?: string;
|
|
665
|
+
name?: string;
|
|
666
|
+
image?: string;
|
|
667
|
+
}
|
|
668
|
+
interface SessionTokens {
|
|
669
|
+
accessToken: string;
|
|
670
|
+
refreshToken: string;
|
|
671
|
+
/** Seconds until the access token expires (mirrors `accessTokenTtl`). */
|
|
672
|
+
expiresIn: number;
|
|
673
|
+
}
|
|
674
|
+
interface VerifiedSession {
|
|
675
|
+
userId: string;
|
|
676
|
+
email?: string;
|
|
677
|
+
name?: string;
|
|
678
|
+
claims: Record<string, unknown>;
|
|
679
|
+
}
|
|
680
|
+
interface JwtSessionModule {
|
|
681
|
+
/**
|
|
682
|
+
* Issue a new access + refresh token pair for the given user.
|
|
683
|
+
*
|
|
684
|
+
* The refresh token is stored hashed. The raw value is returned once and
|
|
685
|
+
* cannot be recovered from the database.
|
|
686
|
+
*/
|
|
687
|
+
createSession(user: SessionUser): Promise<Result<SessionTokens>>;
|
|
688
|
+
/**
|
|
689
|
+
* Verify an access token and return the embedded claims.
|
|
690
|
+
*
|
|
691
|
+
* Does not touch the database. Fails on expiry, wrong signature, or issuer/
|
|
692
|
+
* audience mismatch.
|
|
693
|
+
*/
|
|
694
|
+
verifySession(token: string): Promise<Result<VerifiedSession>>;
|
|
695
|
+
/**
|
|
696
|
+
* Exchange a refresh token for a new access + refresh token pair.
|
|
697
|
+
*
|
|
698
|
+
* The incoming refresh token is soft-revoked (marked used) on success.
|
|
699
|
+
* A brand-new refresh token is issued so that each refresh rotates the token.
|
|
700
|
+
*/
|
|
701
|
+
refreshSession(refreshToken: string): Promise<Result<SessionTokens>>;
|
|
702
|
+
/**
|
|
703
|
+
* Revoke a refresh token, preventing any further refreshes.
|
|
704
|
+
*
|
|
705
|
+
* Calling this on an already-revoked or unknown token is a no-op (returns
|
|
706
|
+
* success) so that logout endpoints are idempotent.
|
|
707
|
+
*/
|
|
708
|
+
revokeSession(refreshToken: string): Promise<Result<void>>;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Create a JWT session module backed by the provided database.
|
|
712
|
+
*
|
|
713
|
+
* The module is stateless beyond the database — multiple instances sharing
|
|
714
|
+
* the same DB are safe and will honour each other's revocations.
|
|
715
|
+
*/
|
|
716
|
+
declare function createJwtSessionModule(config: JwtSessionConfig, db: Database): JwtSessionModule;
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Last login tracking module for KavachOS.
|
|
720
|
+
*
|
|
721
|
+
* Records each successful authentication event per user, capturing the method
|
|
722
|
+
* used, optional IP address, optional user agent, and timestamp. A rolling
|
|
723
|
+
* window of recent logins is kept — older entries beyond the configured limit
|
|
724
|
+
* are pruned on every write so storage stays bounded.
|
|
725
|
+
*
|
|
726
|
+
* @example
|
|
727
|
+
* ```typescript
|
|
728
|
+
* const loginHistory = createLastLoginModule({}, db);
|
|
729
|
+
*
|
|
730
|
+
* // After a successful sign-in
|
|
731
|
+
* await loginHistory.recordLogin({
|
|
732
|
+
* userId: 'usr_123',
|
|
733
|
+
* method: 'magic-link',
|
|
734
|
+
* ip: request.headers.get('x-forwarded-for') ?? undefined,
|
|
735
|
+
* userAgent: request.headers.get('user-agent') ?? undefined,
|
|
736
|
+
* });
|
|
737
|
+
*
|
|
738
|
+
* // Show the user their last login on a security page
|
|
739
|
+
* const result = await loginHistory.getLastLogin('usr_123');
|
|
740
|
+
* if (result.success) {
|
|
741
|
+
* console.log(result.data?.method, result.data?.timestamp);
|
|
742
|
+
* }
|
|
743
|
+
* ```
|
|
744
|
+
*/
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* All supported login methods.
|
|
748
|
+
*
|
|
749
|
+
* For OAuth providers use the `oauth:{provider}` pattern, e.g. `oauth:github`,
|
|
750
|
+
* `oauth:google`, `oauth:microsoft`.
|
|
751
|
+
*/
|
|
752
|
+
type LoginMethod = "email-password" | "magic-link" | "email-otp" | "passkey" | `oauth:${string}` | "username-password" | "phone-sms" | "siwe" | "device-auth" | "anonymous" | "api-key";
|
|
753
|
+
interface LastLoginConfig {
|
|
754
|
+
/**
|
|
755
|
+
* Maximum number of login events to retain per user.
|
|
756
|
+
* Older rows beyond this limit are deleted on every `recordLogin` call.
|
|
757
|
+
* Default: 10.
|
|
758
|
+
*/
|
|
759
|
+
maxHistoryPerUser?: number;
|
|
760
|
+
}
|
|
761
|
+
interface RecordLoginInput {
|
|
762
|
+
userId: string;
|
|
763
|
+
method: LoginMethod;
|
|
764
|
+
/** Caller IP address. Stored as-is; normalise before passing if needed. */
|
|
765
|
+
ip?: string;
|
|
766
|
+
/** Raw value of the User-Agent request header. */
|
|
767
|
+
userAgent?: string;
|
|
768
|
+
}
|
|
769
|
+
interface LoginEvent {
|
|
770
|
+
id: string;
|
|
771
|
+
userId: string;
|
|
772
|
+
method: LoginMethod;
|
|
773
|
+
ip: string | null;
|
|
774
|
+
userAgent: string | null;
|
|
775
|
+
timestamp: Date;
|
|
776
|
+
}
|
|
777
|
+
interface LastLoginModule {
|
|
778
|
+
/**
|
|
779
|
+
* Record a successful login event for a user.
|
|
780
|
+
*
|
|
781
|
+
* If the total stored events for that user exceed `maxHistoryPerUser`, the
|
|
782
|
+
* oldest events are deleted so only the most recent N are kept.
|
|
783
|
+
*/
|
|
784
|
+
recordLogin(input: RecordLoginInput): Promise<Result<LoginEvent>>;
|
|
785
|
+
/**
|
|
786
|
+
* Retrieve the single most recent login event for a user.
|
|
787
|
+
*
|
|
788
|
+
* Returns `null` in `data` when no history exists for the user.
|
|
789
|
+
*/
|
|
790
|
+
getLastLogin(userId: string): Promise<Result<LoginEvent | null>>;
|
|
791
|
+
/**
|
|
792
|
+
* Return login history for a user, newest first.
|
|
793
|
+
*
|
|
794
|
+
* @param userId The user to look up.
|
|
795
|
+
* @param limit Maximum number of events to return. Defaults to `maxHistoryPerUser`.
|
|
796
|
+
*/
|
|
797
|
+
getLoginHistory(userId: string, limit?: number): Promise<Result<LoginEvent[]>>;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Create a last-login tracking module backed by the provided database.
|
|
801
|
+
*
|
|
802
|
+
* The module is stateless — safe to instantiate multiple times against the
|
|
803
|
+
* same database.
|
|
804
|
+
*/
|
|
805
|
+
declare function createLastLoginModule(config: LastLoginConfig, db: Database): LastLoginModule;
|
|
806
|
+
|
|
807
|
+
declare function magicLink(config: MagicLinkConfig): KavachPlugin;
|
|
808
|
+
|
|
809
|
+
interface OAuthProvider {
|
|
810
|
+
/** Machine-readable provider ID, e.g. `'google'`, `'github'`. */
|
|
811
|
+
id: string;
|
|
812
|
+
/** Human-readable provider name. */
|
|
813
|
+
name: string;
|
|
814
|
+
/** Base authorization endpoint URL. */
|
|
815
|
+
authorizationUrl: string;
|
|
816
|
+
/** Token exchange endpoint URL. */
|
|
817
|
+
tokenUrl: string;
|
|
818
|
+
/**
|
|
819
|
+
* User profile endpoint URL.
|
|
820
|
+
* Optional — some providers (e.g. Notion) embed user info in the token
|
|
821
|
+
* response and have no separate endpoint.
|
|
822
|
+
*/
|
|
823
|
+
userInfoUrl: string | undefined;
|
|
824
|
+
/** Effective scopes (defaults merged with any user-supplied extras). */
|
|
825
|
+
scopes: string[];
|
|
826
|
+
/**
|
|
827
|
+
* Build the authorization redirect URL.
|
|
828
|
+
*
|
|
829
|
+
* @param state CSRF state value to be validated on callback.
|
|
830
|
+
* @param codeVerifier PKCE code verifier — the provider derives the challenge.
|
|
831
|
+
* @param redirectUri Callback URL to include in the authorization request.
|
|
832
|
+
*/
|
|
833
|
+
getAuthorizationUrl(state: string, codeVerifier: string, redirectUri: string): Promise<string>;
|
|
834
|
+
/**
|
|
835
|
+
* Exchange an authorization code for tokens.
|
|
836
|
+
*
|
|
837
|
+
* @param code The authorization code received on callback.
|
|
838
|
+
* @param codeVerifier PKCE code verifier used to generate the original challenge.
|
|
839
|
+
* @param redirectUri Must match the URI used in the authorization request.
|
|
840
|
+
*/
|
|
841
|
+
exchangeCode(code: string, codeVerifier: string, redirectUri: string): Promise<OAuthTokens>;
|
|
842
|
+
/**
|
|
843
|
+
* Fetch normalized user profile information from the provider.
|
|
844
|
+
*
|
|
845
|
+
* @param accessToken A valid access token issued by the provider.
|
|
846
|
+
*/
|
|
847
|
+
getUserInfo(accessToken: string): Promise<OAuthUserInfo>;
|
|
848
|
+
}
|
|
849
|
+
interface OAuthTokens {
|
|
850
|
+
accessToken: string;
|
|
851
|
+
refreshToken?: string;
|
|
852
|
+
/** Lifetime of the access token in seconds. */
|
|
853
|
+
expiresIn?: number;
|
|
854
|
+
tokenType: string;
|
|
855
|
+
/** Raw token response from the provider (unparsed claims). */
|
|
856
|
+
raw: Record<string, unknown>;
|
|
857
|
+
}
|
|
858
|
+
interface OAuthUserInfo {
|
|
859
|
+
/** Stable user ID at the provider. */
|
|
860
|
+
id: string;
|
|
861
|
+
/**
|
|
862
|
+
* User email address.
|
|
863
|
+
* Some providers (e.g. Reddit) do not expose email via OAuth; callers must
|
|
864
|
+
* handle the undefined case, typically by requiring a separate email step.
|
|
865
|
+
*/
|
|
866
|
+
email: string | undefined;
|
|
867
|
+
name?: string;
|
|
868
|
+
/** URL to the user's avatar image. */
|
|
869
|
+
avatar?: string;
|
|
870
|
+
/** Full raw response from the provider's user info endpoint. */
|
|
871
|
+
raw: Record<string, unknown>;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* OAuth proxy module for mobile apps.
|
|
876
|
+
*
|
|
877
|
+
* Mobile apps cannot safely store OAuth client secrets. This module acts as a
|
|
878
|
+
* server-side intermediary: the mobile app redirects to the provider via
|
|
879
|
+
* KavachOS, which holds the secret and exchanges the authorization code for
|
|
880
|
+
* tokens on the app's behalf.
|
|
881
|
+
*
|
|
882
|
+
* Flow:
|
|
883
|
+
* 1. Mobile app calls GET /auth/oauth-proxy/start?provider=google&redirect_uri=myapp://callback
|
|
884
|
+
* 2. KavachOS validates redirect_uri, stores proxy state, returns provider auth URL.
|
|
885
|
+
* 3. User authenticates with the provider in a browser.
|
|
886
|
+
* 4. Provider redirects to KavachOS callback with code + state.
|
|
887
|
+
* 5. KavachOS exchanges the code (using the server-held client secret), then
|
|
888
|
+
* redirects the mobile app to its custom scheme URL with tokens as query params.
|
|
889
|
+
*
|
|
890
|
+
* Security:
|
|
891
|
+
* - redirect_uri is validated against an explicit allowlist — no open redirects.
|
|
892
|
+
* - Proxy state is a random UUID stored in memory with a 10-minute TTL.
|
|
893
|
+
* - PKCE passthrough: the mobile app may supply a code_challenge; KavachOS
|
|
894
|
+
* forwards it to the provider and passes the verifier back via the callback.
|
|
895
|
+
*/
|
|
896
|
+
|
|
897
|
+
interface OAuthProxyConfig {
|
|
898
|
+
/**
|
|
899
|
+
* Allowed redirect URIs for mobile apps (e.g. `"myapp://callback"`).
|
|
900
|
+
* Only exact matches or scheme-prefix matches (when entry ends with `://`)
|
|
901
|
+
* are allowed. No wildcards. This is an allowlist — anything not listed
|
|
902
|
+
* will be rejected.
|
|
903
|
+
*/
|
|
904
|
+
allowedRedirectUris: string[];
|
|
905
|
+
/** Rate limit per IP. Defaults to 20 requests per 60 seconds. */
|
|
906
|
+
rateLimit?: {
|
|
907
|
+
max: number;
|
|
908
|
+
windowSeconds: number;
|
|
909
|
+
};
|
|
910
|
+
/**
|
|
911
|
+
* How long a proxy state entry lives in seconds.
|
|
912
|
+
* Defaults to 600 (10 minutes).
|
|
913
|
+
*/
|
|
914
|
+
stateTtlSeconds?: number;
|
|
915
|
+
}
|
|
916
|
+
interface ProxyTokens {
|
|
917
|
+
accessToken: string;
|
|
918
|
+
refreshToken?: string;
|
|
919
|
+
idToken?: string;
|
|
920
|
+
expiresIn?: number;
|
|
921
|
+
}
|
|
922
|
+
interface OAuthProxyModule {
|
|
923
|
+
/**
|
|
924
|
+
* Start the proxy flow.
|
|
925
|
+
*
|
|
926
|
+
* Validates `redirectUri` against the allowlist, generates a PKCE verifier,
|
|
927
|
+
* stores proxy state keyed by an opaque `proxyState` value, and returns the
|
|
928
|
+
* provider authorization URL for the caller to redirect to.
|
|
929
|
+
*
|
|
930
|
+
* @param provider Provider ID (must be in `providers` map).
|
|
931
|
+
* @param redirectUri Mobile app callback URI. Must be in `allowedRedirectUris`.
|
|
932
|
+
* @param state Optional caller-supplied state passed back on completion.
|
|
933
|
+
* @param codeChallenge Optional PKCE code challenge from the mobile app (S256).
|
|
934
|
+
*/
|
|
935
|
+
startFlow(provider: string, redirectUri: string, state?: string, codeChallenge?: string): Promise<{
|
|
936
|
+
authUrl: string;
|
|
937
|
+
proxyState: string;
|
|
938
|
+
}>;
|
|
939
|
+
/**
|
|
940
|
+
* Handle the provider callback.
|
|
941
|
+
*
|
|
942
|
+
* Looks up the stored proxy state, exchanges the code with the provider,
|
|
943
|
+
* and returns the final redirect URL for the mobile app plus the raw tokens.
|
|
944
|
+
*
|
|
945
|
+
* @param code Authorization code from the provider.
|
|
946
|
+
* @param proxyState The opaque state value returned by `startFlow`.
|
|
947
|
+
*/
|
|
948
|
+
handleCallback(code: string, proxyState: string): Promise<{
|
|
949
|
+
redirectUrl: string;
|
|
950
|
+
tokens: ProxyTokens;
|
|
951
|
+
}>;
|
|
952
|
+
/** Route HTTP requests to the proxy endpoints. Returns null if no match. */
|
|
953
|
+
handleRequest(request: Request): Promise<Response | null>;
|
|
954
|
+
}
|
|
955
|
+
declare function createOAuthProxyModule(config: OAuthProxyConfig, providers: Record<string, OAuthProvider>,
|
|
956
|
+
/** Base URL of the KavachOS server, e.g. "https://auth.example.com". */
|
|
957
|
+
baseUrl: string): OAuthProxyModule;
|
|
958
|
+
declare class OAuthProxyError extends Error {
|
|
959
|
+
readonly code: string;
|
|
960
|
+
constructor(code: string, message: string);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
interface OAuthProxyPluginConfig extends OAuthProxyConfig {
|
|
964
|
+
/**
|
|
965
|
+
* Provider instances to make available for the proxy.
|
|
966
|
+
* Keys are the provider IDs used in the `provider` query parameter.
|
|
967
|
+
*
|
|
968
|
+
* @example
|
|
969
|
+
* ```typescript
|
|
970
|
+
* import { createGoogleProvider } from 'kavachos/auth/oauth/providers/google';
|
|
971
|
+
*
|
|
972
|
+
* oauthProxy({
|
|
973
|
+
* providers: {
|
|
974
|
+
* google: createGoogleProvider({ clientId: '...', clientSecret: '...' }),
|
|
975
|
+
* },
|
|
976
|
+
* allowedRedirectUris: ['com.example.app://callback'],
|
|
977
|
+
* })
|
|
978
|
+
* ```
|
|
979
|
+
*/
|
|
980
|
+
providers: Record<string, OAuthProvider>;
|
|
981
|
+
}
|
|
982
|
+
declare function oauthProxy(config: OAuthProxyPluginConfig): KavachPlugin;
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* OIDC Provider module for KavachOS.
|
|
986
|
+
*
|
|
987
|
+
* Turns KavachOS into a full OpenID Connect identity provider (IdP).
|
|
988
|
+
* External applications can register as OIDC clients and authenticate
|
|
989
|
+
* their users against KavachOS using standard authorization code flow
|
|
990
|
+
* with PKCE, ID tokens, refresh tokens, discovery, and JWKS.
|
|
991
|
+
*
|
|
992
|
+
* @example
|
|
993
|
+
* ```typescript
|
|
994
|
+
* import { generateKeyPair } from 'jose';
|
|
995
|
+
* import { createOidcProviderModule } from 'kavachos/auth';
|
|
996
|
+
*
|
|
997
|
+
* const { privateKey } = await generateKeyPair('RS256');
|
|
998
|
+
* const oidc = createOidcProviderModule(
|
|
999
|
+
* {
|
|
1000
|
+
* issuer: 'https://auth.example.com',
|
|
1001
|
+
* signingKey: privateKey,
|
|
1002
|
+
* },
|
|
1003
|
+
* db,
|
|
1004
|
+
* getUserClaims,
|
|
1005
|
+
* );
|
|
1006
|
+
*
|
|
1007
|
+
* // Register a client
|
|
1008
|
+
* const client = await oidc.registerClient({
|
|
1009
|
+
* clientName: 'My App',
|
|
1010
|
+
* redirectUris: ['https://app.example.com/callback'],
|
|
1011
|
+
* });
|
|
1012
|
+
*
|
|
1013
|
+
* // Discovery
|
|
1014
|
+
* const doc = oidc.getDiscoveryDocument();
|
|
1015
|
+
* ```
|
|
1016
|
+
*/
|
|
1017
|
+
|
|
1018
|
+
interface OidcProviderConfig {
|
|
1019
|
+
/** Issuer identifier, e.g. "https://auth.example.com". Must be a URL. */
|
|
1020
|
+
issuer: string;
|
|
1021
|
+
/** Private key used to sign ID tokens and access tokens (RSA or EC). */
|
|
1022
|
+
signingKey: CryptoKey | jose.JWK;
|
|
1023
|
+
/** JWT signing algorithm. Default: 'RS256'. */
|
|
1024
|
+
signingAlgorithm?: string;
|
|
1025
|
+
/** Access token lifetime in seconds. Default: 3600 (1 hour). */
|
|
1026
|
+
accessTokenTtl?: number;
|
|
1027
|
+
/** Refresh token lifetime in seconds. Default: 2592000 (30 days). */
|
|
1028
|
+
refreshTokenTtl?: number;
|
|
1029
|
+
/** Authorization code lifetime in seconds. Default: 600 (10 minutes). */
|
|
1030
|
+
authCodeTtl?: number;
|
|
1031
|
+
/** ID token lifetime in seconds. Default: 3600 (1 hour). */
|
|
1032
|
+
idTokenTtl?: number;
|
|
1033
|
+
/** Scopes this provider supports. Default: ['openid', 'profile', 'email']. */
|
|
1034
|
+
supportedScopes?: string[];
|
|
1035
|
+
}
|
|
1036
|
+
interface RegisterClientInput {
|
|
1037
|
+
clientName: string;
|
|
1038
|
+
redirectUris: string[];
|
|
1039
|
+
grantTypes?: string[];
|
|
1040
|
+
responseTypes?: string[];
|
|
1041
|
+
scopes?: string[];
|
|
1042
|
+
tokenEndpointAuthMethod?: string;
|
|
1043
|
+
}
|
|
1044
|
+
interface OidcClient {
|
|
1045
|
+
clientId: string;
|
|
1046
|
+
clientSecret: string | null;
|
|
1047
|
+
clientName: string;
|
|
1048
|
+
redirectUris: string[];
|
|
1049
|
+
grantTypes: string[];
|
|
1050
|
+
responseTypes: string[];
|
|
1051
|
+
scopes: string[];
|
|
1052
|
+
tokenEndpointAuthMethod: string;
|
|
1053
|
+
createdAt: Date;
|
|
1054
|
+
}
|
|
1055
|
+
interface AuthorizeParams {
|
|
1056
|
+
clientId: string;
|
|
1057
|
+
redirectUri: string;
|
|
1058
|
+
responseType: string;
|
|
1059
|
+
scope: string;
|
|
1060
|
+
state?: string;
|
|
1061
|
+
nonce?: string;
|
|
1062
|
+
codeChallenge?: string;
|
|
1063
|
+
codeChallengeMethod?: string;
|
|
1064
|
+
/** The authenticated user's ID. Must be resolved before calling authorize. */
|
|
1065
|
+
userId: string;
|
|
1066
|
+
}
|
|
1067
|
+
interface TokenParams {
|
|
1068
|
+
grantType: string;
|
|
1069
|
+
code?: string;
|
|
1070
|
+
redirectUri?: string;
|
|
1071
|
+
codeVerifier?: string;
|
|
1072
|
+
refreshToken?: string;
|
|
1073
|
+
clientId: string;
|
|
1074
|
+
clientSecret?: string;
|
|
1075
|
+
}
|
|
1076
|
+
interface TokenResponse {
|
|
1077
|
+
accessToken: string;
|
|
1078
|
+
idToken: string;
|
|
1079
|
+
refreshToken: string;
|
|
1080
|
+
tokenType: "Bearer";
|
|
1081
|
+
expiresIn: number;
|
|
1082
|
+
}
|
|
1083
|
+
interface UserInfoClaims {
|
|
1084
|
+
sub: string;
|
|
1085
|
+
email?: string;
|
|
1086
|
+
name?: string;
|
|
1087
|
+
picture?: string;
|
|
1088
|
+
emailVerified?: boolean;
|
|
1089
|
+
}
|
|
1090
|
+
interface AccessTokenClaims {
|
|
1091
|
+
sub: string;
|
|
1092
|
+
iss: string;
|
|
1093
|
+
aud: string;
|
|
1094
|
+
exp: number;
|
|
1095
|
+
iat: number;
|
|
1096
|
+
jti: string;
|
|
1097
|
+
scope: string;
|
|
1098
|
+
clientId: string;
|
|
1099
|
+
}
|
|
1100
|
+
interface OidcDiscoveryDocument {
|
|
1101
|
+
issuer: string;
|
|
1102
|
+
authorization_endpoint: string;
|
|
1103
|
+
token_endpoint: string;
|
|
1104
|
+
userinfo_endpoint: string;
|
|
1105
|
+
jwks_uri: string;
|
|
1106
|
+
registration_endpoint: string;
|
|
1107
|
+
scopes_supported: string[];
|
|
1108
|
+
response_types_supported: string[];
|
|
1109
|
+
grant_types_supported: string[];
|
|
1110
|
+
subject_types_supported: string[];
|
|
1111
|
+
id_token_signing_alg_values_supported: string[];
|
|
1112
|
+
token_endpoint_auth_methods_supported: string[];
|
|
1113
|
+
claims_supported: string[];
|
|
1114
|
+
code_challenge_methods_supported: string[];
|
|
1115
|
+
}
|
|
1116
|
+
interface JsonWebKeySet {
|
|
1117
|
+
keys: jose.JWK[];
|
|
1118
|
+
}
|
|
1119
|
+
/** Callback to resolve user claims for ID tokens and the userinfo endpoint. */
|
|
1120
|
+
type GetUserClaimsFn = (userId: string, scopes: string[]) => Promise<UserInfoClaims>;
|
|
1121
|
+
interface OidcProviderModule {
|
|
1122
|
+
registerClient(input: RegisterClientInput): Promise<Result<OidcClient>>;
|
|
1123
|
+
getClient(clientId: string): Promise<Result<OidcClient>>;
|
|
1124
|
+
deleteClient(clientId: string): Promise<Result<void>>;
|
|
1125
|
+
authorize(params: AuthorizeParams): Promise<Result<{
|
|
1126
|
+
code: string;
|
|
1127
|
+
state?: string;
|
|
1128
|
+
}>>;
|
|
1129
|
+
exchangeToken(params: TokenParams): Promise<Result<TokenResponse>>;
|
|
1130
|
+
getUserInfo(accessToken: string): Promise<Result<UserInfoClaims>>;
|
|
1131
|
+
getDiscoveryDocument(): OidcDiscoveryDocument;
|
|
1132
|
+
getJwks(): Promise<JsonWebKeySet>;
|
|
1133
|
+
validateAccessToken(token: string): Promise<Result<AccessTokenClaims>>;
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Create an OIDC Provider module that turns KavachOS into an identity provider.
|
|
1137
|
+
*
|
|
1138
|
+
* @param config Provider configuration (issuer, signing key, TTLs).
|
|
1139
|
+
* @param db Drizzle database instance.
|
|
1140
|
+
* @param getUserClaims Callback that resolves user claims given a userId and scopes.
|
|
1141
|
+
*/
|
|
1142
|
+
declare function createOidcProviderModule(config: OidcProviderConfig, db: Database, getUserClaims: GetUserClaimsFn): OidcProviderModule;
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* Google One Tap authentication for KavachOS.
|
|
1146
|
+
*
|
|
1147
|
+
* Verifies a Google ID token (issued by the Google Identity Services JS
|
|
1148
|
+
* library) server-side via Google's public JWKS endpoint. No Google SDK
|
|
1149
|
+
* required. Uses `jose` for JWT verification — the same library used
|
|
1150
|
+
* elsewhere in KavachOS.
|
|
1151
|
+
*
|
|
1152
|
+
* Flow:
|
|
1153
|
+
* 1. Front-end includes the Google Identity Services script and mounts the
|
|
1154
|
+
* One Tap prompt.
|
|
1155
|
+
* 2. On sign-in the browser POSTs the `credential` (ID token) to
|
|
1156
|
+
* POST /auth/one-tap/callback.
|
|
1157
|
+
* 3. This module verifies the token, finds or creates the user, and returns
|
|
1158
|
+
* a session.
|
|
1159
|
+
*
|
|
1160
|
+
* CSRF: Google's JS library sets a `g_csrf_token` cookie and includes the
|
|
1161
|
+
* same value in the POST body. Both must match.
|
|
1162
|
+
*
|
|
1163
|
+
* @example
|
|
1164
|
+
* ```typescript
|
|
1165
|
+
* const kavach = await createKavach({
|
|
1166
|
+
* database: { provider: 'sqlite', url: 'kavach.db' },
|
|
1167
|
+
* auth: { session: { secret: process.env.SESSION_SECRET } },
|
|
1168
|
+
* });
|
|
1169
|
+
*
|
|
1170
|
+
* // Use via plugin
|
|
1171
|
+
* import { oneTap } from 'kavachos/auth';
|
|
1172
|
+
* // plugins: [oneTap({ clientId: process.env.GOOGLE_CLIENT_ID })]
|
|
1173
|
+
*
|
|
1174
|
+
* // Or use the module directly
|
|
1175
|
+
* const tap = createOneTapModule({ clientId: '...' }, db, sessionManager);
|
|
1176
|
+
* const googleUser = await tap.verify(idToken);
|
|
1177
|
+
* ```
|
|
1178
|
+
*/
|
|
1179
|
+
|
|
1180
|
+
interface OneTapConfig {
|
|
1181
|
+
/** Google OAuth client ID */
|
|
1182
|
+
clientId: string;
|
|
1183
|
+
/** Auto-create user if not found (default: true) */
|
|
1184
|
+
autoCreateUser?: boolean;
|
|
1185
|
+
/** CSRF token cookie name (default: "g_csrf_token") */
|
|
1186
|
+
csrfCookieName?: string;
|
|
1187
|
+
}
|
|
1188
|
+
interface GoogleUser {
|
|
1189
|
+
/** Google user ID (stable, use this as the external ID) */
|
|
1190
|
+
sub: string;
|
|
1191
|
+
email: string;
|
|
1192
|
+
emailVerified: boolean;
|
|
1193
|
+
name: string;
|
|
1194
|
+
givenName?: string;
|
|
1195
|
+
familyName?: string;
|
|
1196
|
+
picture?: string;
|
|
1197
|
+
}
|
|
1198
|
+
interface OneTapModule {
|
|
1199
|
+
/**
|
|
1200
|
+
* Verify a Google ID token and return the decoded user claims.
|
|
1201
|
+
* Throws if the token is invalid, expired, or issued for the wrong audience.
|
|
1202
|
+
*/
|
|
1203
|
+
verify(idToken: string): Promise<GoogleUser>;
|
|
1204
|
+
/**
|
|
1205
|
+
* Handle the POST callback from Google's JS library.
|
|
1206
|
+
*
|
|
1207
|
+
* Expects `application/x-www-form-urlencoded` with `credential` and
|
|
1208
|
+
* `g_csrf_token` fields (plus the matching CSRF cookie). Returns a JSON
|
|
1209
|
+
* response with `{ user, session }` on success or null when the path does
|
|
1210
|
+
* not match (allowing fall-through to other handlers).
|
|
1211
|
+
*/
|
|
1212
|
+
handleRequest(request: Request): Promise<Response | null>;
|
|
1213
|
+
}
|
|
1214
|
+
declare class OneTapVerifyError extends Error {
|
|
1215
|
+
readonly code: string;
|
|
1216
|
+
constructor(message: string, code: string);
|
|
1217
|
+
}
|
|
1218
|
+
declare function createOneTapModule(config: OneTapConfig, db: Database, sessionManager: SessionManager): OneTapModule;
|
|
1219
|
+
|
|
1220
|
+
declare function oneTap(config: OneTapConfig): KavachPlugin;
|
|
1221
|
+
|
|
1222
|
+
/**
|
|
1223
|
+
* One-time token module for KavachOS.
|
|
1224
|
+
*
|
|
1225
|
+
* Issues single-use tokens for email verification, password resets,
|
|
1226
|
+
* invitations, and custom flows. The raw token is returned to the caller
|
|
1227
|
+
* once and never persisted — only a SHA-256 hash is stored. Tokens are
|
|
1228
|
+
* invalidated on first use or when they expire.
|
|
1229
|
+
*
|
|
1230
|
+
* @example
|
|
1231
|
+
* ```typescript
|
|
1232
|
+
* const tokens = createOneTimeTokenModule({}, db);
|
|
1233
|
+
*
|
|
1234
|
+
* // Create a password-reset token
|
|
1235
|
+
* const result = await tokens.createToken({
|
|
1236
|
+
* purpose: 'password-reset',
|
|
1237
|
+
* identifier: 'alice@example.com',
|
|
1238
|
+
* ttlSeconds: 1800,
|
|
1239
|
+
* });
|
|
1240
|
+
* if (result.success) {
|
|
1241
|
+
* await mailer.send({ to: 'alice@example.com', token: result.data.token });
|
|
1242
|
+
* }
|
|
1243
|
+
*
|
|
1244
|
+
* // Validate (and consume) on the reset page
|
|
1245
|
+
* const validation = await tokens.validateToken(incomingToken, 'password-reset');
|
|
1246
|
+
* if (validation.success) {
|
|
1247
|
+
* // validation.data.identifier === 'alice@example.com'
|
|
1248
|
+
* }
|
|
1249
|
+
* ```
|
|
1250
|
+
*/
|
|
1251
|
+
|
|
1252
|
+
/** Token purpose discriminator. Use 'custom' for any application-specific flow. */
|
|
1253
|
+
type OneTimeTokenPurpose = "email-verify" | "password-reset" | "invitation" | "custom";
|
|
1254
|
+
interface OneTimeTokenConfig {
|
|
1255
|
+
/** Default TTL in seconds when none is specified per-call. Default: 3600 (1 hour). */
|
|
1256
|
+
defaultTtlSeconds?: number;
|
|
1257
|
+
}
|
|
1258
|
+
interface CreateTokenInput {
|
|
1259
|
+
purpose: OneTimeTokenPurpose;
|
|
1260
|
+
/** Email address, user ID, or any caller-supplied key that scopes the token. */
|
|
1261
|
+
identifier: string;
|
|
1262
|
+
/** Arbitrary data to associate with the token (e.g. org ID, invited role). */
|
|
1263
|
+
metadata?: Record<string, unknown>;
|
|
1264
|
+
/** Override the module-level default TTL for this token only. */
|
|
1265
|
+
ttlSeconds?: number;
|
|
1266
|
+
}
|
|
1267
|
+
interface ValidateTokenResult {
|
|
1268
|
+
identifier: string;
|
|
1269
|
+
metadata?: Record<string, unknown>;
|
|
1270
|
+
}
|
|
1271
|
+
interface RevokeTokensResult {
|
|
1272
|
+
count: number;
|
|
1273
|
+
}
|
|
1274
|
+
interface OneTimeTokenModule {
|
|
1275
|
+
/**
|
|
1276
|
+
* Create a new one-time token.
|
|
1277
|
+
*
|
|
1278
|
+
* Returns the raw token (hex string) exactly once. Store it in your email
|
|
1279
|
+
* or link — it cannot be recovered from the database afterwards.
|
|
1280
|
+
*/
|
|
1281
|
+
createToken(input: CreateTokenInput): Promise<Result<{
|
|
1282
|
+
token: string;
|
|
1283
|
+
expiresAt: Date;
|
|
1284
|
+
}>>;
|
|
1285
|
+
/**
|
|
1286
|
+
* Validate a token and mark it as used.
|
|
1287
|
+
*
|
|
1288
|
+
* Fails when the token is unknown, already used, expired, or belongs to a
|
|
1289
|
+
* different purpose. On success the token is consumed immediately.
|
|
1290
|
+
*/
|
|
1291
|
+
validateToken(token: string, purpose: string): Promise<Result<ValidateTokenResult>>;
|
|
1292
|
+
/**
|
|
1293
|
+
* Revoke all active tokens for an identifier, optionally scoped to a purpose.
|
|
1294
|
+
*
|
|
1295
|
+
* Useful for invalidating outstanding password-reset links when a user
|
|
1296
|
+
* changes their password through another flow, or for cleaning up on account
|
|
1297
|
+
* deletion.
|
|
1298
|
+
*/
|
|
1299
|
+
revokeTokens(identifier: string, purpose?: string): Promise<Result<RevokeTokensResult>>;
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Create a one-time token module backed by the provided database.
|
|
1303
|
+
*
|
|
1304
|
+
* The module is stateless — no in-memory caches — so multiple instances
|
|
1305
|
+
* sharing the same database are safe.
|
|
1306
|
+
*/
|
|
1307
|
+
declare function createOneTimeTokenModule(config: OneTimeTokenConfig, db: Database): OneTimeTokenModule;
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* OpenAPI 3.1 spec generation plugin for KavachOS.
|
|
1311
|
+
*
|
|
1312
|
+
* Generates a complete OpenAPI document from KavachOS's registered auth
|
|
1313
|
+
* endpoints. Useful for serving at `/api/kavach/openapi.json` or wiring
|
|
1314
|
+
* into Swagger UI / Scalar.
|
|
1315
|
+
*
|
|
1316
|
+
* @example
|
|
1317
|
+
* ```typescript
|
|
1318
|
+
* import { createOpenApiModule } from 'kavachos/auth';
|
|
1319
|
+
*
|
|
1320
|
+
* const openapi = createOpenApiModule();
|
|
1321
|
+
* const spec = openapi.generateSpec({
|
|
1322
|
+
* title: 'My App Auth API',
|
|
1323
|
+
* serverUrl: 'https://api.example.com',
|
|
1324
|
+
* include: ['auth', 'sessions', 'api-keys'],
|
|
1325
|
+
* });
|
|
1326
|
+
*
|
|
1327
|
+
* // In your request handler:
|
|
1328
|
+
* const response = openapi.handleRequest(request);
|
|
1329
|
+
* if (response) return response;
|
|
1330
|
+
* ```
|
|
1331
|
+
*/
|
|
1332
|
+
type EndpointGroup = "agents" | "auth" | "oauth" | "mcp" | "admin" | "organizations" | "sessions" | "api-keys" | "webhooks";
|
|
1333
|
+
interface OpenApiConfig {
|
|
1334
|
+
/** API title shown in spec. Default: "KavachOS API" */
|
|
1335
|
+
title?: string;
|
|
1336
|
+
/** Spec version string. Default: "0.0.1" */
|
|
1337
|
+
version?: string;
|
|
1338
|
+
/** Short description of the API */
|
|
1339
|
+
description?: string;
|
|
1340
|
+
/** Server base URL. Default: "/" */
|
|
1341
|
+
serverUrl?: string;
|
|
1342
|
+
/** Path prefix for all KavachOS endpoints. Default: "/api/kavach" */
|
|
1343
|
+
basePath?: string;
|
|
1344
|
+
/**
|
|
1345
|
+
* Limit which endpoint groups are included in the spec.
|
|
1346
|
+
* When omitted all groups are included.
|
|
1347
|
+
*/
|
|
1348
|
+
include?: EndpointGroup[];
|
|
1349
|
+
}
|
|
1350
|
+
interface OpenApiInfo {
|
|
1351
|
+
title: string;
|
|
1352
|
+
version: string;
|
|
1353
|
+
description?: string;
|
|
1354
|
+
}
|
|
1355
|
+
interface OpenApiServer {
|
|
1356
|
+
url: string;
|
|
1357
|
+
}
|
|
1358
|
+
interface OpenApiSchema {
|
|
1359
|
+
type?: string;
|
|
1360
|
+
properties?: Record<string, OpenApiSchema>;
|
|
1361
|
+
required?: string[];
|
|
1362
|
+
items?: OpenApiSchema;
|
|
1363
|
+
description?: string;
|
|
1364
|
+
example?: unknown;
|
|
1365
|
+
enum?: unknown[];
|
|
1366
|
+
format?: string;
|
|
1367
|
+
nullable?: boolean;
|
|
1368
|
+
additionalProperties?: boolean | OpenApiSchema;
|
|
1369
|
+
oneOf?: OpenApiSchema[];
|
|
1370
|
+
}
|
|
1371
|
+
interface OpenApiMediaType {
|
|
1372
|
+
schema: OpenApiSchema;
|
|
1373
|
+
}
|
|
1374
|
+
interface OpenApiRequestBody {
|
|
1375
|
+
required?: boolean;
|
|
1376
|
+
content: Record<string, OpenApiMediaType>;
|
|
1377
|
+
}
|
|
1378
|
+
interface OpenApiResponse {
|
|
1379
|
+
description: string;
|
|
1380
|
+
content?: Record<string, OpenApiMediaType>;
|
|
1381
|
+
}
|
|
1382
|
+
interface OpenApiSecurityRequirement {
|
|
1383
|
+
[schemeName: string]: string[];
|
|
1384
|
+
}
|
|
1385
|
+
interface OpenApiOperation {
|
|
1386
|
+
operationId: string;
|
|
1387
|
+
summary: string;
|
|
1388
|
+
tags: string[];
|
|
1389
|
+
security?: OpenApiSecurityRequirement[];
|
|
1390
|
+
requestBody?: OpenApiRequestBody;
|
|
1391
|
+
responses: Record<string, OpenApiResponse>;
|
|
1392
|
+
parameters?: OpenApiParameter[];
|
|
1393
|
+
}
|
|
1394
|
+
interface OpenApiParameter {
|
|
1395
|
+
name: string;
|
|
1396
|
+
in: "path" | "query" | "header" | "cookie";
|
|
1397
|
+
required?: boolean;
|
|
1398
|
+
schema: OpenApiSchema;
|
|
1399
|
+
description?: string;
|
|
1400
|
+
}
|
|
1401
|
+
interface OpenApiPathItem {
|
|
1402
|
+
get?: OpenApiOperation;
|
|
1403
|
+
post?: OpenApiOperation;
|
|
1404
|
+
put?: OpenApiOperation;
|
|
1405
|
+
patch?: OpenApiOperation;
|
|
1406
|
+
delete?: OpenApiOperation;
|
|
1407
|
+
}
|
|
1408
|
+
interface OpenApiSecurityScheme {
|
|
1409
|
+
type: string;
|
|
1410
|
+
scheme?: string;
|
|
1411
|
+
bearerFormat?: string;
|
|
1412
|
+
description?: string;
|
|
1413
|
+
in?: string;
|
|
1414
|
+
name?: string;
|
|
1415
|
+
flows?: Record<string, unknown>;
|
|
1416
|
+
}
|
|
1417
|
+
interface OpenApiComponents {
|
|
1418
|
+
securitySchemes: Record<string, OpenApiSecurityScheme>;
|
|
1419
|
+
schemas: Record<string, OpenApiSchema>;
|
|
1420
|
+
}
|
|
1421
|
+
interface OpenApiDocument {
|
|
1422
|
+
openapi: "3.1.0";
|
|
1423
|
+
info: OpenApiInfo;
|
|
1424
|
+
servers: OpenApiServer[];
|
|
1425
|
+
paths: Record<string, OpenApiPathItem>;
|
|
1426
|
+
components: OpenApiComponents;
|
|
1427
|
+
tags: Array<{
|
|
1428
|
+
name: string;
|
|
1429
|
+
description?: string;
|
|
1430
|
+
}>;
|
|
1431
|
+
}
|
|
1432
|
+
interface OpenApiModule {
|
|
1433
|
+
/** Generate a complete OpenAPI 3.1.0 document */
|
|
1434
|
+
generateSpec(config?: OpenApiConfig): OpenApiDocument;
|
|
1435
|
+
/**
|
|
1436
|
+
* Handle an HTTP request for the spec JSON.
|
|
1437
|
+
*
|
|
1438
|
+
* Returns a `Response` with the spec when the request path ends with
|
|
1439
|
+
* `/openapi.json`. Returns `null` for any other path so the caller's
|
|
1440
|
+
* routing continues normally.
|
|
1441
|
+
*/
|
|
1442
|
+
handleRequest(request: Request, config?: OpenApiConfig): Response | null;
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Create an OpenAPI module that generates KavachOS API specifications.
|
|
1446
|
+
*
|
|
1447
|
+
* The returned module is stateless and safe to share across requests.
|
|
1448
|
+
*/
|
|
1449
|
+
declare function createOpenApiModule(): OpenApiModule;
|
|
1450
|
+
|
|
1451
|
+
declare function organization(config?: OrgConfig): KavachPlugin;
|
|
1452
|
+
|
|
1453
|
+
declare function passkey(config: PasskeyConfig): KavachPlugin;
|
|
1454
|
+
|
|
1455
|
+
/**
|
|
1456
|
+
* Polar payment integration for KavachOS.
|
|
1457
|
+
*
|
|
1458
|
+
* Links Polar customers to KavachOS users, handles subscription lifecycle
|
|
1459
|
+
* webhooks, and stores subscription status. Uses Polar's REST API directly
|
|
1460
|
+
* via fetch — no Polar SDK dependency.
|
|
1461
|
+
*
|
|
1462
|
+
* @example
|
|
1463
|
+
* ```typescript
|
|
1464
|
+
* import { createPolarModule } from 'kavachos/auth';
|
|
1465
|
+
*
|
|
1466
|
+
* const polar = createPolarModule({
|
|
1467
|
+
* accessToken: process.env.POLAR_ACCESS_TOKEN!,
|
|
1468
|
+
* webhookSecret: process.env.POLAR_WEBHOOK_SECRET!,
|
|
1469
|
+
* sandbox: process.env.NODE_ENV !== 'production',
|
|
1470
|
+
* onSubscriptionChange: async (userId, sub) => {
|
|
1471
|
+
* console.log(`User ${userId} subscription: ${sub.status}`);
|
|
1472
|
+
* },
|
|
1473
|
+
* }, db);
|
|
1474
|
+
*
|
|
1475
|
+
* const { url } = await polar.createCheckout(userId, 'product_xxx', {
|
|
1476
|
+
* successUrl: 'https://example.com/success',
|
|
1477
|
+
* });
|
|
1478
|
+
* ```
|
|
1479
|
+
*/
|
|
1480
|
+
|
|
1481
|
+
interface PolarConfig {
|
|
1482
|
+
/** Polar access token */
|
|
1483
|
+
accessToken: string;
|
|
1484
|
+
/** Polar webhook secret for HMAC-SHA256 signature verification */
|
|
1485
|
+
webhookSecret: string;
|
|
1486
|
+
/** Optional organization ID to scope requests */
|
|
1487
|
+
organizationId?: string;
|
|
1488
|
+
/** Use sandbox.polar.sh instead of polar.sh (default: false) */
|
|
1489
|
+
sandbox?: boolean;
|
|
1490
|
+
/** Callback fired whenever subscription data changes for a user */
|
|
1491
|
+
onSubscriptionChange?: (userId: string, subscription: PolarSubscription) => Promise<void>;
|
|
1492
|
+
}
|
|
1493
|
+
interface PolarSubscription {
|
|
1494
|
+
id: string;
|
|
1495
|
+
status: "active" | "canceled" | "incomplete" | "past_due" | "trialing" | "unpaid";
|
|
1496
|
+
productId: string;
|
|
1497
|
+
currentPeriodEnd: Date;
|
|
1498
|
+
cancelAtPeriodEnd: boolean;
|
|
1499
|
+
}
|
|
1500
|
+
interface PolarModule {
|
|
1501
|
+
/** Create a Polar checkout session and return its URL + ID */
|
|
1502
|
+
createCheckout(userId: string, productId: string, options?: {
|
|
1503
|
+
successUrl?: string;
|
|
1504
|
+
customerEmail?: string;
|
|
1505
|
+
}): Promise<{
|
|
1506
|
+
url: string;
|
|
1507
|
+
id: string;
|
|
1508
|
+
}>;
|
|
1509
|
+
/** Return current subscription info for a user from the database */
|
|
1510
|
+
getSubscription(userId: string): Promise<PolarSubscription | null>;
|
|
1511
|
+
/** Verify Polar webhook signature and dispatch to internal handlers */
|
|
1512
|
+
handleWebhook(request: Request): Promise<Response>;
|
|
1513
|
+
/** Route an incoming HTTP request to the appropriate handler (returns null if path unmatched) */
|
|
1514
|
+
handleRequest(request: Request): Promise<Response | null>;
|
|
1515
|
+
}
|
|
1516
|
+
declare function createPolarModule(config: PolarConfig, db: Database): PolarModule;
|
|
1517
|
+
|
|
1518
|
+
declare function polar(config: PolarConfig): KavachPlugin;
|
|
1519
|
+
|
|
1520
|
+
/**
|
|
1521
|
+
* In-memory sliding window rate limiter for auth endpoints.
|
|
1522
|
+
*
|
|
1523
|
+
* Uses a Map keyed by the caller-supplied string (typically an IP address).
|
|
1524
|
+
* Each entry stores a list of request timestamps within the current window.
|
|
1525
|
+
* Expired timestamps are pruned on every check so memory stays bounded.
|
|
1526
|
+
*/
|
|
1527
|
+
interface RateLimitConfig {
|
|
1528
|
+
/** Max requests allowed within the window */
|
|
1529
|
+
max: number;
|
|
1530
|
+
/** Window duration in seconds */
|
|
1531
|
+
window: number;
|
|
1532
|
+
}
|
|
1533
|
+
interface RateLimitResult {
|
|
1534
|
+
allowed: boolean;
|
|
1535
|
+
/** Requests remaining in the current window */
|
|
1536
|
+
remaining: number;
|
|
1537
|
+
/** When the oldest in-window request expires and a slot re-opens */
|
|
1538
|
+
resetAt: Date;
|
|
1539
|
+
}
|
|
1540
|
+
interface RateLimiter {
|
|
1541
|
+
/** Check whether the key is within its limit. Consumes one slot when allowed. */
|
|
1542
|
+
check(key: string): RateLimitResult;
|
|
1543
|
+
/** Clear all recorded hits for a key (useful in tests or on successful auth). */
|
|
1544
|
+
reset(key: string): void;
|
|
1545
|
+
}
|
|
1546
|
+
declare function createRateLimiter(config: RateLimitConfig): RateLimiter;
|
|
1547
|
+
|
|
1548
|
+
/**
|
|
1549
|
+
* Higher-order function that wraps a plugin endpoint handler with IP-based
|
|
1550
|
+
* rate limiting. When the limit is exceeded it responds with 429 and a
|
|
1551
|
+
* Retry-After header before the wrapped handler is ever called.
|
|
1552
|
+
*/
|
|
1553
|
+
|
|
1554
|
+
interface RateLimitMiddlewareOptions {
|
|
1555
|
+
/**
|
|
1556
|
+
* Derive the rate-limit key from the incoming request.
|
|
1557
|
+
*
|
|
1558
|
+
* Defaults to the first non-empty value of:
|
|
1559
|
+
* x-forwarded-for → first IP in the comma-separated list
|
|
1560
|
+
* x-real-ip
|
|
1561
|
+
* "unknown"
|
|
1562
|
+
*/
|
|
1563
|
+
keyExtractor?: (request: Request) => string;
|
|
1564
|
+
}
|
|
1565
|
+
declare function withRateLimit(handler: PluginEndpoint["handler"], limiter: RateLimiter, options?: RateLimitMiddlewareOptions): PluginEndpoint["handler"];
|
|
1566
|
+
|
|
1567
|
+
/**
|
|
1568
|
+
* SCIM 2.0 directory sync for KavachOS.
|
|
1569
|
+
*
|
|
1570
|
+
* Implements RFC 7644 (SCIM 2.0 protocol) to allow enterprise identity
|
|
1571
|
+
* providers (Okta, Azure AD, Google Workspace) to provision and deprovision
|
|
1572
|
+
* users and groups automatically.
|
|
1573
|
+
*
|
|
1574
|
+
* Users map to kavach_users. Groups map to kavach_organizations.
|
|
1575
|
+
*
|
|
1576
|
+
* @example
|
|
1577
|
+
* ```typescript
|
|
1578
|
+
* import { createScimModule } from 'kavachos/auth';
|
|
1579
|
+
*
|
|
1580
|
+
* const scim = createScimModule({
|
|
1581
|
+
* bearerToken: process.env.SCIM_TOKEN,
|
|
1582
|
+
* onProvision: async (user) => {
|
|
1583
|
+
* console.log('Provisioned:', user.userName);
|
|
1584
|
+
* },
|
|
1585
|
+
* }, db);
|
|
1586
|
+
*
|
|
1587
|
+
* // In your request handler:
|
|
1588
|
+
* const response = await scim.handleRequest(request);
|
|
1589
|
+
* ```
|
|
1590
|
+
*/
|
|
1591
|
+
|
|
1592
|
+
interface ScimConfig {
|
|
1593
|
+
/** Bearer token for SCIM API authentication */
|
|
1594
|
+
bearerToken: string;
|
|
1595
|
+
/** Whether to auto-create users on provision (default: true) */
|
|
1596
|
+
autoCreateUsers?: boolean;
|
|
1597
|
+
/** Whether to auto-deactivate users on deprovision (default: true) */
|
|
1598
|
+
autoDeactivateUsers?: boolean;
|
|
1599
|
+
/** Callback when user is provisioned */
|
|
1600
|
+
onProvision?: (user: ScimUser) => Promise<void>;
|
|
1601
|
+
/** Callback when user is deprovisioned */
|
|
1602
|
+
onDeprovision?: (userId: string) => Promise<void>;
|
|
1603
|
+
}
|
|
1604
|
+
interface ScimUser {
|
|
1605
|
+
id: string;
|
|
1606
|
+
userName: string;
|
|
1607
|
+
name?: {
|
|
1608
|
+
givenName?: string;
|
|
1609
|
+
familyName?: string;
|
|
1610
|
+
};
|
|
1611
|
+
emails?: Array<{
|
|
1612
|
+
value: string;
|
|
1613
|
+
primary?: boolean;
|
|
1614
|
+
}>;
|
|
1615
|
+
active?: boolean;
|
|
1616
|
+
externalId?: string;
|
|
1617
|
+
}
|
|
1618
|
+
interface ScimGroup {
|
|
1619
|
+
id: string;
|
|
1620
|
+
displayName: string;
|
|
1621
|
+
externalId?: string;
|
|
1622
|
+
members?: Array<{
|
|
1623
|
+
value: string;
|
|
1624
|
+
display?: string;
|
|
1625
|
+
}>;
|
|
1626
|
+
}
|
|
1627
|
+
interface ScimModule {
|
|
1628
|
+
handleRequest(request: Request): Promise<Response | null>;
|
|
1629
|
+
}
|
|
1630
|
+
declare function createScimModule(config: ScimConfig, db: Database): ScimModule;
|
|
1631
|
+
|
|
1632
|
+
/**
|
|
1633
|
+
* SCIM 2.0 directory sync plugin for KavachOS.
|
|
1634
|
+
*
|
|
1635
|
+
* Mounts SCIM endpoints under `/scim/v2/` so enterprise IdPs (Okta, Azure AD,
|
|
1636
|
+
* Google Workspace) can provision and deprovision users and groups.
|
|
1637
|
+
*
|
|
1638
|
+
* All endpoints require a static Bearer token supplied via `config.bearerToken`.
|
|
1639
|
+
*
|
|
1640
|
+
* @example
|
|
1641
|
+
* ```typescript
|
|
1642
|
+
* import { createKavach } from 'kavachos';
|
|
1643
|
+
* import { scim } from 'kavachos/auth';
|
|
1644
|
+
*
|
|
1645
|
+
* const kavach = await createKavach({
|
|
1646
|
+
* database: { provider: 'sqlite', url: 'kavach.db' },
|
|
1647
|
+
* plugins: [
|
|
1648
|
+
* scim({
|
|
1649
|
+
* bearerToken: process.env.SCIM_TOKEN,
|
|
1650
|
+
* onProvision: async (user) => {
|
|
1651
|
+
* await sendWelcomeEmail(user.emails?.[0]?.value);
|
|
1652
|
+
* },
|
|
1653
|
+
* }),
|
|
1654
|
+
* ],
|
|
1655
|
+
* });
|
|
1656
|
+
* ```
|
|
1657
|
+
*/
|
|
1658
|
+
declare function scim(config: ScimConfig): KavachPlugin;
|
|
1659
|
+
|
|
1660
|
+
/**
|
|
1661
|
+
* Sign In With Ethereum (SIWE) for KavachOS.
|
|
1662
|
+
*
|
|
1663
|
+
* Authenticates users by verifying an Ethereum wallet signature per EIP-4361.
|
|
1664
|
+
* Full secp256k1 recovery requires ethers or viem as peer deps — this module
|
|
1665
|
+
* validates message format and nonce integrity. Add a `verify` override via
|
|
1666
|
+
* `verifySignature` option when you want cryptographic proof.
|
|
1667
|
+
*
|
|
1668
|
+
* @example
|
|
1669
|
+
* ```typescript
|
|
1670
|
+
* const siwe = createSiweModule({
|
|
1671
|
+
* domain: 'example.com',
|
|
1672
|
+
* uri: 'https://example.com',
|
|
1673
|
+
* statement: 'Sign in to Example App',
|
|
1674
|
+
* });
|
|
1675
|
+
*
|
|
1676
|
+
* // 1. Frontend requests a nonce
|
|
1677
|
+
* const nonce = await siwe.generateNonce();
|
|
1678
|
+
*
|
|
1679
|
+
* // 2. Frontend builds the message and asks wallet to sign it
|
|
1680
|
+
* const message = siwe.buildMessage('0xAbc...', nonce, 1);
|
|
1681
|
+
*
|
|
1682
|
+
* // 3. Frontend submits message + signature
|
|
1683
|
+
* const { address, chainId } = await siwe.verify(message, signature);
|
|
1684
|
+
* ```
|
|
1685
|
+
*/
|
|
1686
|
+
interface SiweConfig {
|
|
1687
|
+
/** Your app's domain (e.g., "example.com") */
|
|
1688
|
+
domain: string;
|
|
1689
|
+
/** URI (e.g., "https://example.com") */
|
|
1690
|
+
uri: string;
|
|
1691
|
+
/** Statement shown in wallet (optional) */
|
|
1692
|
+
statement?: string;
|
|
1693
|
+
/** Nonce TTL in seconds (default: 300) */
|
|
1694
|
+
nonceTtlSeconds?: number;
|
|
1695
|
+
/**
|
|
1696
|
+
* Optional signature verifier. Called with the EIP-4361 message and
|
|
1697
|
+
* hex signature. Should return the recovered Ethereum address.
|
|
1698
|
+
* When omitted, the module trusts the address from the message body
|
|
1699
|
+
* (suitable for development; add viem/ethers recovery in production).
|
|
1700
|
+
*/
|
|
1701
|
+
verifySignature?: (message: string, signature: string) => Promise<string>;
|
|
1702
|
+
}
|
|
1703
|
+
interface SiweModule {
|
|
1704
|
+
/** Generate a nonce for the SIWE message */
|
|
1705
|
+
generateNonce(): Promise<string>;
|
|
1706
|
+
/** Build the EIP-4361 message for the wallet to sign */
|
|
1707
|
+
buildMessage(address: string, nonce: string, chainId?: number): string;
|
|
1708
|
+
/** Verify the signed message and return the Ethereum address */
|
|
1709
|
+
verify(message: string, signature: string): Promise<{
|
|
1710
|
+
address: string;
|
|
1711
|
+
chainId: number;
|
|
1712
|
+
}>;
|
|
1713
|
+
/** Handle full SIWE auth flow via HTTP */
|
|
1714
|
+
handleRequest(request: Request): Promise<Response | null>;
|
|
1715
|
+
}
|
|
1716
|
+
interface SiweVerifyResult {
|
|
1717
|
+
address: string;
|
|
1718
|
+
chainId: number;
|
|
1719
|
+
}
|
|
1720
|
+
declare function createSiweModule(config: SiweConfig): SiweModule;
|
|
1721
|
+
|
|
1722
|
+
declare function siwe(config: SiweConfig): KavachPlugin;
|
|
1723
|
+
|
|
1724
|
+
/**
|
|
1725
|
+
* Stripe payment integration for KavachOS.
|
|
1726
|
+
*
|
|
1727
|
+
* Links Stripe customers to KavachOS users, handles subscription lifecycle
|
|
1728
|
+
* webhooks, and stores subscription status. Uses Stripe's REST API directly
|
|
1729
|
+
* via fetch — no Stripe SDK dependency.
|
|
1730
|
+
*
|
|
1731
|
+
* @example
|
|
1732
|
+
* ```typescript
|
|
1733
|
+
* import { createStripeModule } from 'kavachos/auth';
|
|
1734
|
+
*
|
|
1735
|
+
* const stripe = createStripeModule({
|
|
1736
|
+
* secretKey: process.env.STRIPE_SECRET_KEY!,
|
|
1737
|
+
* webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
|
|
1738
|
+
* autoCreateCustomer: true,
|
|
1739
|
+
* }, db);
|
|
1740
|
+
*
|
|
1741
|
+
* const customerId = await stripe.createCustomer(userId, email, name);
|
|
1742
|
+
* const { url } = await stripe.createCheckoutSession(userId, priceId, {
|
|
1743
|
+
* successUrl: 'https://example.com/success',
|
|
1744
|
+
* cancelUrl: 'https://example.com/cancel',
|
|
1745
|
+
* });
|
|
1746
|
+
* ```
|
|
1747
|
+
*/
|
|
1748
|
+
|
|
1749
|
+
interface StripeConfig {
|
|
1750
|
+
/** Stripe secret key (sk_live_... or sk_test_...) */
|
|
1751
|
+
secretKey: string;
|
|
1752
|
+
/** Stripe webhook signing secret (whsec_...) */
|
|
1753
|
+
webhookSecret: string;
|
|
1754
|
+
/** Auto-create a Stripe customer when the user record is referenced (default: true) */
|
|
1755
|
+
autoCreateCustomer?: boolean;
|
|
1756
|
+
/** Stripe API version (default: "2024-12-18.acacia") */
|
|
1757
|
+
apiVersion?: string;
|
|
1758
|
+
/** Callback fired whenever subscription data changes for a user */
|
|
1759
|
+
onSubscriptionChange?: (userId: string, subscription: SubscriptionInfo) => Promise<void>;
|
|
1760
|
+
}
|
|
1761
|
+
interface SubscriptionInfo {
|
|
1762
|
+
id: string;
|
|
1763
|
+
status: "active" | "canceled" | "past_due" | "trialing" | "unpaid" | "incomplete";
|
|
1764
|
+
priceId: string;
|
|
1765
|
+
currentPeriodEnd: Date;
|
|
1766
|
+
cancelAtPeriodEnd: boolean;
|
|
1767
|
+
}
|
|
1768
|
+
interface CheckoutOptions {
|
|
1769
|
+
successUrl?: string;
|
|
1770
|
+
cancelUrl?: string;
|
|
1771
|
+
trialDays?: number;
|
|
1772
|
+
metadata?: Record<string, string>;
|
|
1773
|
+
}
|
|
1774
|
+
interface StripeModule {
|
|
1775
|
+
/** Create a Stripe customer for a user and persist the customer ID */
|
|
1776
|
+
createCustomer(userId: string, email: string, name?: string): Promise<string>;
|
|
1777
|
+
/** Get the Stripe customer ID stored for a user */
|
|
1778
|
+
getCustomerId(userId: string): Promise<string | null>;
|
|
1779
|
+
/** Create a Stripe Checkout Session and return its URL + ID */
|
|
1780
|
+
createCheckoutSession(userId: string, priceId: string, options?: CheckoutOptions): Promise<{
|
|
1781
|
+
url: string;
|
|
1782
|
+
sessionId: string;
|
|
1783
|
+
}>;
|
|
1784
|
+
/** Create a Stripe Billing Portal session and return its URL */
|
|
1785
|
+
createPortalSession(userId: string, returnUrl: string): Promise<{
|
|
1786
|
+
url: string;
|
|
1787
|
+
}>;
|
|
1788
|
+
/** Return current subscription info for a user from the database */
|
|
1789
|
+
getSubscription(userId: string): Promise<SubscriptionInfo | null>;
|
|
1790
|
+
/** Verify Stripe webhook signature and dispatch to internal handlers */
|
|
1791
|
+
handleWebhook(request: Request): Promise<Response>;
|
|
1792
|
+
/** Route an incoming HTTP request to the appropriate handler (returns null if path unmatched) */
|
|
1793
|
+
handleRequest(request: Request): Promise<Response | null>;
|
|
1794
|
+
}
|
|
1795
|
+
declare function createStripeModule(config: StripeConfig, db: Database): StripeModule;
|
|
1796
|
+
|
|
1797
|
+
declare function stripe(config: StripeConfig): KavachPlugin;
|
|
1798
|
+
|
|
1799
|
+
type TwoFactorConfig = TotpConfig;
|
|
1800
|
+
declare function twoFactor(config?: TwoFactorConfig): KavachPlugin;
|
|
1801
|
+
|
|
1802
|
+
/**
|
|
1803
|
+
* Trusted device windows for 2FA in KavachOS.
|
|
1804
|
+
*
|
|
1805
|
+
* After a successful 2FA challenge, a device can be marked as trusted.
|
|
1806
|
+
* Subsequent logins from the same device skip 2FA until the trust window
|
|
1807
|
+
* expires (default 30 days) or trust is explicitly revoked.
|
|
1808
|
+
*
|
|
1809
|
+
* Fingerprints are HMAC-signed to prevent client-side spoofing.
|
|
1810
|
+
*/
|
|
1811
|
+
|
|
1812
|
+
interface TrustedDeviceConfig {
|
|
1813
|
+
/** How long to trust a device in seconds (default: 30 days). */
|
|
1814
|
+
trustDurationSeconds?: number;
|
|
1815
|
+
/** Maximum number of trusted devices per user (default: 10). */
|
|
1816
|
+
maxDevices?: number;
|
|
1817
|
+
/**
|
|
1818
|
+
* Secret used to HMAC-sign fingerprints, preventing client spoofing.
|
|
1819
|
+
* Should be a stable application secret (e.g. from env). If omitted a
|
|
1820
|
+
* random per-process key is used (fingerprints won't survive restarts).
|
|
1821
|
+
*/
|
|
1822
|
+
secret?: string;
|
|
1823
|
+
}
|
|
1824
|
+
interface TrustedDevice {
|
|
1825
|
+
id: string;
|
|
1826
|
+
fingerprint: string;
|
|
1827
|
+
label: string;
|
|
1828
|
+
trustedAt: Date;
|
|
1829
|
+
expiresAt: Date;
|
|
1830
|
+
}
|
|
1831
|
+
interface TrustedDeviceModule {
|
|
1832
|
+
/**
|
|
1833
|
+
* Mark the device identified by `deviceFingerprint` as trusted for
|
|
1834
|
+
* `userId`. Returns a stable trust token (the record id) that can be
|
|
1835
|
+
* stored in a long-lived cookie.
|
|
1836
|
+
*/
|
|
1837
|
+
trustDevice(userId: string, deviceFingerprint: string): Promise<string>;
|
|
1838
|
+
/** Returns true if the device is currently trusted (not expired). */
|
|
1839
|
+
isTrusted(userId: string, deviceFingerprint: string): Promise<boolean>;
|
|
1840
|
+
/** Remove trust for a single device. */
|
|
1841
|
+
revokeDevice(userId: string, deviceFingerprint: string): Promise<void>;
|
|
1842
|
+
/** Remove trust for every device belonging to a user. */
|
|
1843
|
+
revokeAllDevices(userId: string): Promise<void>;
|
|
1844
|
+
/** List all active trusted devices for a user. */
|
|
1845
|
+
listDevices(userId: string): Promise<TrustedDevice[]>;
|
|
1846
|
+
/**
|
|
1847
|
+
* Derive a stable, HMAC-protected fingerprint from request headers.
|
|
1848
|
+
* The same request will always produce the same fingerprint; changing
|
|
1849
|
+
* user-agent or accept-language invalidates the fingerprint.
|
|
1850
|
+
*/
|
|
1851
|
+
generateFingerprint(request: Request): string;
|
|
1852
|
+
}
|
|
1853
|
+
declare function createTrustedDeviceModule(config: TrustedDeviceConfig, db: Database): TrustedDeviceModule;
|
|
1854
|
+
/**
|
|
1855
|
+
* Derive a human-readable device label from a request's User-Agent header.
|
|
1856
|
+
* Useful when calling `trustDevice` so the stored label is descriptive.
|
|
1857
|
+
*/
|
|
1858
|
+
declare function deviceLabelFromRequest(request: Request): string;
|
|
1859
|
+
|
|
1860
|
+
export { type AccessTokenClaims, type AdditionalFieldsConfig, type AdditionalFieldsModule, AdminConfig, type AnonymousAuthConfig, type AnonymousAuthModule, ApiKeyManagerConfig, AuthAdapter, type AuthorizeParams, type BearerAuthOptions, type CheckoutOptions, type CreateTokenInput, type CustomSessionConfig, type CustomSessionModule, type DeleteOptions, type DeleteResult, type DeviceAuthConfig, type DeviceAuthModule, type DeviceAuthStatus, type DeviceCodeResponse, EmailOtpConfig, type EndpointGroup, type FieldDefinition, type GdprModule, type GetUserClaimsFn, type GoogleUser, type HeaderAuthOptions, HibpApiError, HibpBreachedError, type HibpConfig, type HibpModule, type JsonWebKeySet, type JwtSessionConfig, type JwtSessionModule, type LastLoginConfig, type LastLoginModule, type LoginEvent, type LoginMethod, MagicLinkConfig, type OAuthProxyConfig, OAuthProxyError, type OAuthProxyModule, type OAuthProxyPluginConfig, type OidcClient, type OidcDiscoveryDocument, type OidcProviderConfig, type OidcProviderModule, type OneTapConfig, type OneTapModule, OneTapVerifyError, type OneTimeTokenConfig, type OneTimeTokenModule, type OneTimeTokenPurpose, type OpenApiComponents, type OpenApiConfig, type OpenApiDocument, type OpenApiInfo, type OpenApiMediaType, type OpenApiModule, type OpenApiOperation, type OpenApiParameter, type OpenApiPathItem, type OpenApiRequestBody, type OpenApiResponse, type OpenApiSchema, type OpenApiSecurityRequirement, type OpenApiSecurityScheme, type OpenApiServer, OrgConfig, PasskeyConfig, type PolarConfig, type PolarModule, type PolarSubscription, type ProxyTokens, type RateLimitConfig, type RateLimitMiddlewareOptions, type RateLimitResult, type RateLimiter, type RecordLoginInput, type RegisterClientInput, ResolvedUser, type RevokeTokensResult, type ScimConfig, type ScimGroup, type ScimModule, type ScimUser, type SessionTokens, type SessionUser, type SiweConfig, type SiweModule, type SiweVerifyResult, type StripeConfig, type StripeModule, type SubscriptionInfo, type TokenParams, type TokenResponse, TotpConfig, type TrustedDevice, type TrustedDeviceConfig, type TrustedDeviceModule, type TwoFactorConfig, type UserDataExport, type UserInfoClaims, type ValidateTokenResult, type ValidationResult, type VerifiedSession, additionalFields, admin, anonymousAuth, apiKeys, bearerAuth, createAdditionalFieldsModule, createAnonymousAuthModule, createCustomSessionModule, createDeviceAuthModule, createGdprModule, createHibpModule, createJwtSessionModule, createLastLoginModule, createOAuthProxyModule, createOidcProviderModule, createOneTapModule, createOneTimeTokenModule, createOpenApiModule, createPolarModule, createRateLimiter, createScimModule, createSiweModule, createStripeModule, createTrustedDeviceModule, customAuth, customSession, deviceAuth, deviceLabelFromRequest, emailOtp, gdpr, headerAuth, magicLink, oauthProxy, oneTap, organization, passkey, polar, scim, siwe, stripe, twoFactor, withRateLimit };
|