@c9up/warden 0.1.4 → 0.1.6

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.
Files changed (88) hide show
  1. package/README.md +58 -0
  2. package/dist/Guard.d.ts +9 -0
  3. package/dist/Guard.d.ts.map +1 -1
  4. package/dist/Guard.js +16 -0
  5. package/dist/Guard.js.map +1 -1
  6. package/dist/RedisBlacklistDriver.d.ts +32 -0
  7. package/dist/RedisBlacklistDriver.d.ts.map +1 -0
  8. package/dist/RedisBlacklistDriver.js +37 -0
  9. package/dist/RedisBlacklistDriver.js.map +1 -0
  10. package/dist/ResilientBlacklistDriver.d.ts +35 -0
  11. package/dist/ResilientBlacklistDriver.d.ts.map +1 -0
  12. package/dist/ResilientBlacklistDriver.js +61 -0
  13. package/dist/ResilientBlacklistDriver.js.map +1 -0
  14. package/dist/TokenBlacklist.d.ts +6 -2
  15. package/dist/TokenBlacklist.d.ts.map +1 -1
  16. package/dist/TokenBlacklist.js +2 -2
  17. package/dist/TokenBlacklist.js.map +1 -1
  18. package/dist/WardenProvider.d.ts.map +1 -1
  19. package/dist/WardenProvider.js +47 -6
  20. package/dist/WardenProvider.js.map +1 -1
  21. package/dist/config.d.ts +65 -1
  22. package/dist/config.d.ts.map +1 -1
  23. package/dist/config.js.map +1 -1
  24. package/dist/index.d.ts +15 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +8 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/mfa/BackupCodesProvider.d.ts +39 -0
  29. package/dist/mfa/BackupCodesProvider.d.ts.map +1 -0
  30. package/dist/mfa/BackupCodesProvider.js +87 -0
  31. package/dist/mfa/BackupCodesProvider.js.map +1 -0
  32. package/dist/mfa/MfaManager.d.ts +107 -0
  33. package/dist/mfa/MfaManager.d.ts.map +1 -0
  34. package/dist/mfa/MfaManager.js +219 -0
  35. package/dist/mfa/MfaManager.js.map +1 -0
  36. package/dist/mfa/OtpProvider.d.ts +67 -0
  37. package/dist/mfa/OtpProvider.d.ts.map +1 -0
  38. package/dist/mfa/OtpProvider.js +104 -0
  39. package/dist/mfa/OtpProvider.js.map +1 -0
  40. package/dist/mfa/TotpProvider.d.ts +52 -0
  41. package/dist/mfa/TotpProvider.d.ts.map +1 -0
  42. package/dist/mfa/TotpProvider.js +103 -0
  43. package/dist/mfa/TotpProvider.js.map +1 -0
  44. package/dist/mfa/WebauthnProvider.d.ts +175 -0
  45. package/dist/mfa/WebauthnProvider.d.ts.map +1 -0
  46. package/dist/mfa/WebauthnProvider.js +239 -0
  47. package/dist/mfa/WebauthnProvider.js.map +1 -0
  48. package/dist/mfa/base32.d.ts +13 -0
  49. package/dist/mfa/base32.d.ts.map +1 -0
  50. package/dist/mfa/base32.js +52 -0
  51. package/dist/mfa/base32.js.map +1 -0
  52. package/dist/mfa/webauthn-codec.d.ts +58 -0
  53. package/dist/mfa/webauthn-codec.d.ts.map +1 -0
  54. package/dist/mfa/webauthn-codec.js +221 -0
  55. package/dist/mfa/webauthn-codec.js.map +1 -0
  56. package/dist/middleware.d.ts +31 -0
  57. package/dist/middleware.d.ts.map +1 -1
  58. package/dist/middleware.js +77 -4
  59. package/dist/middleware.js.map +1 -1
  60. package/dist/standalone.d.ts.map +1 -1
  61. package/dist/standalone.js +12 -0
  62. package/dist/standalone.js.map +1 -1
  63. package/dist/strategies/ApiKeyStrategy.d.ts.map +1 -1
  64. package/dist/strategies/ApiKeyStrategy.js +14 -1
  65. package/dist/strategies/ApiKeyStrategy.js.map +1 -1
  66. package/index.darwin-arm64.node +0 -0
  67. package/index.darwin-x64.node +0 -0
  68. package/index.linux-arm64-gnu.node +0 -0
  69. package/index.linux-x64-gnu.node +0 -0
  70. package/index.win32-x64-msvc.node +0 -0
  71. package/package.json +2 -2
  72. package/src/Guard.ts +21 -0
  73. package/src/RedisBlacklistDriver.ts +60 -0
  74. package/src/ResilientBlacklistDriver.ts +84 -0
  75. package/src/TokenBlacklist.ts +7 -3
  76. package/src/WardenProvider.ts +53 -7
  77. package/src/config.ts +60 -1
  78. package/src/index.ts +58 -0
  79. package/src/mfa/BackupCodesProvider.ts +125 -0
  80. package/src/mfa/MfaManager.ts +307 -0
  81. package/src/mfa/OtpProvider.ts +177 -0
  82. package/src/mfa/TotpProvider.ts +140 -0
  83. package/src/mfa/WebauthnProvider.ts +416 -0
  84. package/src/mfa/base32.ts +54 -0
  85. package/src/mfa/webauthn-codec.ts +264 -0
  86. package/src/middleware.ts +111 -2
  87. package/src/standalone.ts +14 -1
  88. package/src/strategies/ApiKeyStrategy.ts +14 -1
package/README.md CHANGED
@@ -27,8 +27,66 @@ class OrderController {
27
27
  - Multi-strategy AuthManager (JWT, session, API key, OAuth)
28
28
  - `@Guard()`, `@Permission()`, `@Role()` decorators
29
29
  - RBAC: `hasRole`, `hasPermission`, `hasAllPermissions`
30
+ - Unified authorization: Bouncer-shaped abilities & policies over one resolver — RBAC + ACL + ownership + multi-tenant scope, zero `@adonisjs/bouncer` dependency
30
31
  - Strategy exception safety (catch → AuthResult)
31
32
  - Runtime strategy registration
33
+ - **Multi-factor authentication** — TOTP, backup codes, email/SMS OTP, WebAuthn/passkeys, all dependency-free
34
+ - JWT revocation blacklist (Memory / Redis-KeyDB / resilient fallback drivers)
35
+
36
+ ## Multi-factor authentication
37
+
38
+ Four providers, all implemented on `node:crypto` with **zero third-party dependency**
39
+ (WebAuthn included). Every provider and the manager take pluggable stores, defaulting
40
+ to in-memory — supply persistent stores (Atlas, KeyDB…) in production.
41
+
42
+ ```typescript
43
+ import {
44
+ MfaManager,
45
+ TotpProvider,
46
+ BackupCodesProvider,
47
+ RequireMfa,
48
+ } from '@c9up/warden'
49
+
50
+ const mfa = new MfaManager({
51
+ issuer: 'Fluveo',
52
+ totp: new TotpProvider(),
53
+ backupCodes: new BackupCodesProvider(),
54
+ // store: new AtlasMfaFactorStore(db), // persistent factors in production
55
+ rateLimit: { maxAttempts: 5, windowSeconds: 900 }, // per-user brute-force lock
56
+ })
57
+
58
+ // Enroll (returns the otpauth:// URI to render as a QR code)
59
+ const { factorId, uri } = await mfa.enrollTotp({ id: user.id, name: user.email })
60
+ await mfa.confirmTotp(factorId, codeFromAuthenticatorApp) // → true once confirmed
61
+ const recoveryCodes = await mfa.createBackupCodes(user.id) // show once
62
+
63
+ // At sign-in step-up: TOTP code OR a backup code (rate-limited)
64
+ if (await mfa.verify(user.id, submittedCode)) {
65
+ // issue a JWT carrying `mfa: true`
66
+ }
67
+ ```
68
+
69
+ Gate sensitive routes — the middleware returns `403 MFA_REQUIRED` unless the
70
+ authenticated user's payload carries a truthy `mfa` claim:
71
+
72
+ ```typescript
73
+ class TransferController {
74
+ @Guard('jwt')
75
+ @RequireMfa()
76
+ async transfer() { /* only reachable after MFA step-up */ }
77
+ }
78
+ ```
79
+
80
+ Wire the manager into the container via config:
81
+
82
+ ```typescript
83
+ // config/auth.ts
84
+ import { defineConfig } from '@c9up/warden/config'
85
+ export default defineConfig({ jwt: { /* … */ }, mfa: { manager: mfa } })
86
+ ```
87
+
88
+ Email/SMS OTP (`OtpProvider`) and WebAuthn/passkeys (`WebauthnProvider`) are used
89
+ directly — OTP needs a delivery channel, WebAuthn persists passkeys in its own store.
32
90
 
33
91
  ## License
34
92
 
package/dist/Guard.d.ts CHANGED
@@ -23,4 +23,13 @@ export declare function getGuardMetadata(target: object, propertyKey: string | s
23
23
  export declare function getPermissionMetadata(target: object, propertyKey: string | symbol): string[];
24
24
  /** Get role metadata. */
25
25
  export declare function getRoleMetadata(target: object, propertyKey: string | symbol): string[];
26
+ /**
27
+ * @RequireMfa() — require the authenticated user to have completed multi-factor
28
+ * authentication. The auth middleware rejects with 403 `MFA_REQUIRED` when the
29
+ * user payload lacks a truthy `mfa` claim (set by your MFA step-up flow once
30
+ * `MfaManager.verify()` succeeds).
31
+ */
32
+ export declare function RequireMfa(): MethodDecorator;
33
+ /** Whether the route requires completed MFA. */
34
+ export declare function getRequireMfaMetadata(target: object, propertyKey: string | symbol): boolean;
26
35
  //# sourceMappingURL=Guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Guard.d.ts","sourceRoot":"","sources":["../src/Guard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,kBAAkB,CAAC;AAO1B;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CAKvE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,eAAe,CAIpE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,eAAe,CAIxD;AAED,0BAA0B;AAC1B,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,MAAM,EAAE,CAEV;AAED,+BAA+B;AAC/B,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,MAAM,EAAE,CAEV;AAED,yBAAyB;AACzB,wBAAgB,eAAe,CAC9B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,MAAM,EAAE,CAEV"}
1
+ {"version":3,"file":"Guard.d.ts","sourceRoot":"","sources":["../src/Guard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,kBAAkB,CAAC;AAQ1B;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CAKvE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,eAAe,CAIpE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,eAAe,CAIxD;AAED,0BAA0B;AAC1B,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,MAAM,EAAE,CAEV;AAED,+BAA+B;AAC/B,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,MAAM,EAAE,CAEV;AAED,yBAAyB;AACzB,wBAAgB,eAAe,CAC9B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,MAAM,EAAE,CAEV;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,eAAe,CAI5C;AAED,gDAAgD;AAChD,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,OAAO,CAET"}
package/dist/Guard.js CHANGED
@@ -11,6 +11,7 @@ import "reflect-metadata";
11
11
  const GUARD_KEY = Symbol.for("warden:guard");
12
12
  const PERMISSION_KEY = Symbol.for("warden:permission");
13
13
  const ROLE_KEY = Symbol.for("warden:role");
14
+ const MFA_KEY = Symbol.for("warden:mfa");
14
15
  /**
15
16
  * @Guard('jwt') — require authentication via the named strategy.
16
17
  * At least one strategy name is required.
@@ -49,4 +50,19 @@ export function getPermissionMetadata(target, propertyKey) {
49
50
  export function getRoleMetadata(target, propertyKey) {
50
51
  return Reflect.getOwnMetadata(ROLE_KEY, target, propertyKey) ?? [];
51
52
  }
53
+ /**
54
+ * @RequireMfa() — require the authenticated user to have completed multi-factor
55
+ * authentication. The auth middleware rejects with 403 `MFA_REQUIRED` when the
56
+ * user payload lacks a truthy `mfa` claim (set by your MFA step-up flow once
57
+ * `MfaManager.verify()` succeeds).
58
+ */
59
+ export function RequireMfa() {
60
+ return (target, propertyKey) => {
61
+ Reflect.defineMetadata(MFA_KEY, true, target, propertyKey);
62
+ };
63
+ }
64
+ /** Whether the route requires completed MFA. */
65
+ export function getRequireMfaMetadata(target, propertyKey) {
66
+ return Reflect.getOwnMetadata(MFA_KEY, target, propertyKey) ?? false;
67
+ }
52
68
  //# sourceMappingURL=Guard.js.map
package/dist/Guard.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Guard.js","sourceRoot":"","sources":["../src/Guard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,gFAAgF;AAChF,0EAA0E;AAC1E,0EAA0E;AAC1E,OAAO,kBAAkB,CAAC;AAE1B,oFAAoF;AACpF,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAC7C,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AAE3C;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,KAAa,EAAE,GAAG,IAAc;IACrD,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACpC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;QAC9B,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IACpE,CAAC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,GAAG,WAAqB;IAClD,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;QAC9B,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC1E,CAAC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,IAAI,CAAC,GAAG,KAAe;IACtC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;QAC9B,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9D,CAAC,CAAC;AACH,CAAC;AAED,0BAA0B;AAC1B,MAAM,UAAU,gBAAgB,CAC/B,MAAc,EACd,WAA4B;IAE5B,OAAO,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;AACrE,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,qBAAqB,CACpC,MAAc,EACd,WAA4B;IAE5B,OAAO,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;AAC1E,CAAC;AAED,yBAAyB;AACzB,MAAM,UAAU,eAAe,CAC9B,MAAc,EACd,WAA4B;IAE5B,OAAO,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;AACpE,CAAC"}
1
+ {"version":3,"file":"Guard.js","sourceRoot":"","sources":["../src/Guard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,gFAAgF;AAChF,0EAA0E;AAC1E,0EAA0E;AAC1E,OAAO,kBAAkB,CAAC;AAE1B,oFAAoF;AACpF,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAC7C,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,KAAa,EAAE,GAAG,IAAc;IACrD,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACpC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;QAC9B,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IACpE,CAAC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,GAAG,WAAqB;IAClD,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;QAC9B,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC1E,CAAC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,IAAI,CAAC,GAAG,KAAe;IACtC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;QAC9B,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9D,CAAC,CAAC;AACH,CAAC;AAED,0BAA0B;AAC1B,MAAM,UAAU,gBAAgB,CAC/B,MAAc,EACd,WAA4B;IAE5B,OAAO,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;AACrE,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,qBAAqB,CACpC,MAAc,EACd,WAA4B;IAE5B,OAAO,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;AAC1E,CAAC;AAED,yBAAyB;AACzB,MAAM,UAAU,eAAe,CAC9B,MAAc,EACd,WAA4B;IAE5B,OAAO,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;AACpE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU;IACzB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;QAC9B,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC5D,CAAC,CAAC;AACH,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,qBAAqB,CACpC,MAAc,EACd,WAA4B;IAE5B,OAAO,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,KAAK,CAAC;AACtE,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * RedisBlacklistDriver — a persistent JWT blacklist backed by Redis / KeyDB,
3
+ * so logout revocation survives a process restart (unlike MemoryBlacklistDriver).
4
+ *
5
+ * Agnostic by construction: it depends on no Redis package. The caller injects
6
+ * any ioredis-compatible client (KeyDB, Redis, a mock…) that exposes `set` with
7
+ * an `"EX"` ttl and `exists`. Native key expiry does the housekeeping, so
8
+ * `cleanup()` is a no-op.
9
+ */
10
+ import type { BlacklistDriver } from "./TokenBlacklist.js";
11
+ /** The minimal ioredis-shaped surface this driver needs. */
12
+ export interface RedisLikeClient {
13
+ set(key: string, value: string, mode: "EX", ttlSeconds: number): Promise<unknown>;
14
+ exists(key: string): Promise<number>;
15
+ }
16
+ export interface RedisBlacklistConfig {
17
+ /** Key prefix for blacklist entries. Default `warden:blacklist:`. */
18
+ prefix?: string;
19
+ }
20
+ export declare class RedisBlacklistDriver implements BlacklistDriver {
21
+ #private;
22
+ constructor(client: RedisLikeClient, config?: RedisBlacklistConfig);
23
+ /**
24
+ * Blacklist a JTI until its natural expiry. Tokens already past `expiresAt`
25
+ * are skipped — there is nothing left to revoke.
26
+ */
27
+ add(jti: string, expiresAt: number): Promise<void>;
28
+ has(jti: string): Promise<boolean>;
29
+ /** No-op: Redis/KeyDB evicts expired keys on its own. */
30
+ cleanup(): Promise<void>;
31
+ }
32
+ //# sourceMappingURL=RedisBlacklistDriver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RedisBlacklistDriver.d.ts","sourceRoot":"","sources":["../src/RedisBlacklistDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC/B,GAAG,CACF,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACpC,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,oBAAqB,YAAW,eAAe;;gBAI/C,MAAM,EAAE,eAAe,EAAE,MAAM,GAAE,oBAAyB;IAKtE;;;OAGG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxC,yDAAyD;IACnD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAK9B"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * RedisBlacklistDriver — a persistent JWT blacklist backed by Redis / KeyDB,
3
+ * so logout revocation survives a process restart (unlike MemoryBlacklistDriver).
4
+ *
5
+ * Agnostic by construction: it depends on no Redis package. The caller injects
6
+ * any ioredis-compatible client (KeyDB, Redis, a mock…) that exposes `set` with
7
+ * an `"EX"` ttl and `exists`. Native key expiry does the housekeeping, so
8
+ * `cleanup()` is a no-op.
9
+ */
10
+ export class RedisBlacklistDriver {
11
+ #client;
12
+ #prefix;
13
+ constructor(client, config = {}) {
14
+ this.#client = client;
15
+ this.#prefix = config.prefix ?? "warden:blacklist:";
16
+ }
17
+ /**
18
+ * Blacklist a JTI until its natural expiry. Tokens already past `expiresAt`
19
+ * are skipped — there is nothing left to revoke.
20
+ */
21
+ async add(jti, expiresAt) {
22
+ const ttlSeconds = Math.ceil((expiresAt - Date.now()) / 1000);
23
+ if (ttlSeconds <= 0) {
24
+ return;
25
+ }
26
+ await this.#client.set(this.#key(jti), "1", "EX", ttlSeconds);
27
+ }
28
+ async has(jti) {
29
+ return (await this.#client.exists(this.#key(jti))) > 0;
30
+ }
31
+ /** No-op: Redis/KeyDB evicts expired keys on its own. */
32
+ async cleanup() { }
33
+ #key(jti) {
34
+ return `${this.#prefix}${jti}`;
35
+ }
36
+ }
37
+ //# sourceMappingURL=RedisBlacklistDriver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RedisBlacklistDriver.js","sourceRoot":"","sources":["../src/RedisBlacklistDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH,MAAM,OAAO,oBAAoB;IACvB,OAAO,CAAkB;IACzB,OAAO,CAAS;IAEzB,YAAY,MAAuB,EAAE,SAA+B,EAAE;QACrE,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,mBAAmB,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,SAAiB;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QAC9D,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;YACrB,OAAO;QACR,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACpB,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACxD,CAAC;IAED,yDAAyD;IACzD,KAAK,CAAC,OAAO,KAAmB,CAAC;IAEjC,IAAI,CAAC,GAAW;QACf,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;IAChC,CAAC;CACD"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * ResilientBlacklistDriver — wraps a primary blacklist (e.g. KeyDB-backed
3
+ * `RedisBlacklistDriver`) with an in-memory fallback so a cache outage never
4
+ * takes the whole app down.
5
+ *
6
+ * Policy (fail-open on reads):
7
+ * - `add()` — if the primary throws, the revocation is recorded in the
8
+ * fallback so logout stays effective on this instance.
9
+ * - `has()` — if the primary throws, consult the fallback: a token revoked
10
+ * during the outage is still caught here; anything else is
11
+ * allowed through (fail-open). Access tokens are short-lived, so
12
+ * the window a missed revocation survives is bounded by the token
13
+ * TTL — the blacklist is defense-in-depth above expiry, not the
14
+ * primary boundary.
15
+ *
16
+ * Every degradation invokes `onDegrade` (defaults to a stderr warning) so the
17
+ * outage is observable.
18
+ */
19
+ import { type BlacklistDriver } from "./TokenBlacklist.js";
20
+ export interface BlacklistDegradeEvent {
21
+ op: "add" | "has" | "cleanup";
22
+ error: unknown;
23
+ }
24
+ export interface ResilientBlacklistConfig {
25
+ /** Invoked whenever the primary driver fails and the fallback is used. */
26
+ onDegrade?: (event: BlacklistDegradeEvent) => void;
27
+ }
28
+ export declare class ResilientBlacklistDriver implements BlacklistDriver {
29
+ #private;
30
+ constructor(primary: BlacklistDriver, fallback?: BlacklistDriver, config?: ResilientBlacklistConfig);
31
+ add(jti: string, expiresAt: number): Promise<void>;
32
+ has(jti: string): Promise<boolean>;
33
+ cleanup(): Promise<void>;
34
+ }
35
+ //# sourceMappingURL=ResilientBlacklistDriver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResilientBlacklistDriver.d.ts","sourceRoot":"","sources":["../src/ResilientBlacklistDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACN,KAAK,eAAe,EAEpB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;IAC9B,KAAK,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACxC,0EAA0E;IAC1E,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD;AASD,qBAAa,wBAAyB,YAAW,eAAe;;gBAM9D,OAAO,EAAE,eAAe,EACxB,QAAQ,GAAE,eAA6C,EACvD,MAAM,GAAE,wBAA6B;IAOhC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASlD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ9B"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * ResilientBlacklistDriver — wraps a primary blacklist (e.g. KeyDB-backed
3
+ * `RedisBlacklistDriver`) with an in-memory fallback so a cache outage never
4
+ * takes the whole app down.
5
+ *
6
+ * Policy (fail-open on reads):
7
+ * - `add()` — if the primary throws, the revocation is recorded in the
8
+ * fallback so logout stays effective on this instance.
9
+ * - `has()` — if the primary throws, consult the fallback: a token revoked
10
+ * during the outage is still caught here; anything else is
11
+ * allowed through (fail-open). Access tokens are short-lived, so
12
+ * the window a missed revocation survives is bounded by the token
13
+ * TTL — the blacklist is defense-in-depth above expiry, not the
14
+ * primary boundary.
15
+ *
16
+ * Every degradation invokes `onDegrade` (defaults to a stderr warning) so the
17
+ * outage is observable.
18
+ */
19
+ import { MemoryBlacklistDriver, } from "./TokenBlacklist.js";
20
+ function defaultOnDegrade({ op, error }) {
21
+ console.warn(`[warden] blacklist primary failed on ${op}; using in-memory fallback:`, error);
22
+ }
23
+ export class ResilientBlacklistDriver {
24
+ #primary;
25
+ #fallback;
26
+ #onDegrade;
27
+ constructor(primary, fallback = new MemoryBlacklistDriver(), config = {}) {
28
+ this.#primary = primary;
29
+ this.#fallback = fallback;
30
+ this.#onDegrade = config.onDegrade ?? defaultOnDegrade;
31
+ }
32
+ async add(jti, expiresAt) {
33
+ try {
34
+ await this.#primary.add(jti, expiresAt);
35
+ }
36
+ catch (error) {
37
+ this.#onDegrade({ op: "add", error });
38
+ await this.#fallback.add(jti, expiresAt);
39
+ }
40
+ }
41
+ async has(jti) {
42
+ try {
43
+ return await this.#primary.has(jti);
44
+ }
45
+ catch (error) {
46
+ this.#onDegrade({ op: "has", error });
47
+ // Fail-open: honour in-outage revocations on this instance, allow rest.
48
+ return this.#fallback.has(jti);
49
+ }
50
+ }
51
+ async cleanup() {
52
+ try {
53
+ await this.#primary.cleanup?.();
54
+ }
55
+ catch (error) {
56
+ this.#onDegrade({ op: "cleanup", error });
57
+ }
58
+ await this.#fallback.cleanup?.();
59
+ }
60
+ }
61
+ //# sourceMappingURL=ResilientBlacklistDriver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResilientBlacklistDriver.js","sourceRoot":"","sources":["../src/ResilientBlacklistDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAEN,qBAAqB,GACrB,MAAM,qBAAqB,CAAC;AAY7B,SAAS,gBAAgB,CAAC,EAAE,EAAE,EAAE,KAAK,EAAyB;IAC7D,OAAO,CAAC,IAAI,CACX,wCAAwC,EAAE,6BAA6B,EACvE,KAAK,CACL,CAAC;AACH,CAAC;AAED,MAAM,OAAO,wBAAwB;IAC3B,QAAQ,CAAkB;IAC1B,SAAS,CAAkB;IAC3B,UAAU,CAAyC;IAE5D,YACC,OAAwB,EACxB,WAA4B,IAAI,qBAAqB,EAAE,EACvD,SAAmC,EAAE;QAErC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC1B,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,SAAS,IAAI,gBAAgB,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,SAAiB;QACvC,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACtC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC1C,CAAC;IACF,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACpB,IAAI,CAAC;YACJ,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACtC,wEAAwE;YACxE,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;IACF,CAAC;IAED,KAAK,CAAC,OAAO;QACZ,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;IAClC,CAAC;CACD"}
@@ -8,7 +8,11 @@
8
8
  export interface BlacklistDriver {
9
9
  add(jti: string, expiresAt: number): Promise<void>;
10
10
  has(jti: string): Promise<boolean>;
11
- cleanup(): Promise<void>;
11
+ /**
12
+ * Reap expired entries. Optional: TTL-backed stores (Redis/KeyDB) expire keys
13
+ * themselves and have nothing to sweep, so they may omit this.
14
+ */
15
+ cleanup?(): Promise<void>;
12
16
  }
13
17
  /** In-memory blacklist for development. */
14
18
  export declare class MemoryBlacklistDriver implements BlacklistDriver {
@@ -24,7 +28,7 @@ export declare class TokenBlacklist {
24
28
  revoke(jti: string, expiresAt: number): Promise<void>;
25
29
  /** Check if a token JTI is blacklisted. */
26
30
  isRevoked(jti: string): Promise<boolean>;
27
- /** Remove expired entries. */
31
+ /** Remove expired entries (no-op when the driver self-expires via TTL). */
28
32
  cleanup(): Promise<void>;
29
33
  }
30
34
  //# sourceMappingURL=TokenBlacklist.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TokenBlacklist.d.ts","sourceRoot":"","sources":["../src/TokenBlacklist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,eAAe;IAC/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED,2CAA2C;AAC3C,qBAAa,qBAAsB,YAAW,eAAe;IAC5D,OAAO,CAAC,OAAO,CAAkC;IAE3C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAM9B;AAED,qBAAa,cAAc;IACd,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,eAAe;IAE3C,uCAAuC;IACjC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,2CAA2C;IACrC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI9C,8BAA8B;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG9B"}
1
+ {"version":3,"file":"TokenBlacklist.d.ts","sourceRoot":"","sources":["../src/TokenBlacklist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,eAAe;IAC/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC;;;OAGG;IACH,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,2CAA2C;AAC3C,qBAAa,qBAAsB,YAAW,eAAe;IAC5D,OAAO,CAAC,OAAO,CAAkC;IAE3C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAM9B;AAED,qBAAa,cAAc;IACd,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,eAAe;IAE3C,uCAAuC;IACjC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,2CAA2C;IACrC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI9C,2EAA2E;IACrE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG9B"}
@@ -42,9 +42,9 @@ export class TokenBlacklist {
42
42
  async isRevoked(jti) {
43
43
  return this.driver.has(jti);
44
44
  }
45
- /** Remove expired entries. */
45
+ /** Remove expired entries (no-op when the driver self-expires via TTL). */
46
46
  async cleanup() {
47
- return this.driver.cleanup();
47
+ await this.driver.cleanup?.();
48
48
  }
49
49
  }
50
50
  //# sourceMappingURL=TokenBlacklist.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TokenBlacklist.js","sourceRoot":"","sources":["../src/TokenBlacklist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,2CAA2C;AAC3C,MAAM,OAAO,qBAAqB;IACzB,OAAO,GAAwB,IAAI,GAAG,EAAE,CAAC;IAEjD,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,SAAiB;QACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC1C,IAAI,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,CAAC,OAAO;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,GAAG,GAAG,GAAG;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzC,CAAC;IACF,CAAC;CACD;AAED,MAAM,OAAO,cAAc;IACN;IAApB,YAAoB,MAAuB;QAAvB,WAAM,GAAN,MAAM,CAAiB;IAAG,CAAC;IAE/C,uCAAuC;IACvC,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,SAAiB;QAC1C,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,2CAA2C;IAC3C,KAAK,CAAC,SAAS,CAAC,GAAW;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,8BAA8B;IAC9B,KAAK,CAAC,OAAO;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;CACD"}
1
+ {"version":3,"file":"TokenBlacklist.js","sourceRoot":"","sources":["../src/TokenBlacklist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAYH,2CAA2C;AAC3C,MAAM,OAAO,qBAAqB;IACzB,OAAO,GAAwB,IAAI,GAAG,EAAE,CAAC;IAEjD,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,SAAiB;QACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACpB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC1C,IAAI,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,CAAC,OAAO;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,GAAG,GAAG,GAAG;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzC,CAAC;IACF,CAAC;CACD;AAED,MAAM,OAAO,cAAc;IACN;IAApB,YAAoB,MAAuB;QAAvB,WAAM,GAAN,MAAM,CAAiB;IAAG,CAAC;IAE/C,uCAAuC;IACvC,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,SAAiB;QAC1C,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,2CAA2C;IAC3C,KAAK,CAAC,SAAS,CAAC,GAAW;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,2EAA2E;IAC3E,KAAK,CAAC,OAAO;QACZ,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;IAC/B,CAAC;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"WardenProvider.d.ts","sourceRoot":"","sources":["../src/WardenProvider.ts"],"names":[],"mappings":"AAQA,UAAU,eAAe;IACxB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CACjC;AAED,UAAU,iBAAiB;IAC1B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,eAAe,CAAC;IAC3B,MAAM,EAAE,iBAAiB,CAAC;CAC1B;AAED,MAAM,CAAC,OAAO,OAAO,cAAc;IACtB,SAAS,CAAC,GAAG,EAAE,gBAAgB;gBAArB,GAAG,EAAE,gBAAgB;IAE3C,QAAQ;IAuDF,IAAI;CAGV"}
1
+ {"version":3,"file":"WardenProvider.d.ts","sourceRoot":"","sources":["../src/WardenProvider.ts"],"names":[],"mappings":"AAYA,UAAU,eAAe;IACxB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CACjC;AAED,UAAU,iBAAiB;IAC1B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,eAAe,CAAC;IAC3B,MAAM,EAAE,iBAAiB,CAAC;CAC1B;AAED,MAAM,CAAC,OAAO,OAAO,cAAc;IACtB,SAAS,CAAC,GAAG,EAAE,gBAAgB;gBAArB,GAAG,EAAE,gBAAgB;IAE3C,QAAQ;IAiGF,IAAI;CAGV"}
@@ -1,9 +1,12 @@
1
1
  import { AuthManager } from "./AuthManager.js";
2
2
  import { WardenError } from "./errors.js";
3
+ import { MfaManager } from "./mfa/MfaManager.js";
3
4
  import { MemoryRightsStore } from "./rights/MemoryRightsStore.js";
4
5
  import { RightsResolver } from "./rights/RightsResolver.js";
5
6
  import { setAuth } from "./services/main.js";
7
+ import { ApiKeyStrategy } from "./strategies/ApiKeyStrategy.js";
6
8
  import { JwtStrategy } from "./strategies/JwtStrategy.js";
9
+ import { SessionStrategy } from "./strategies/SessionStrategy.js";
7
10
  export default class WardenProvider {
8
11
  app;
9
12
  constructor(app) {
@@ -20,11 +23,29 @@ export default class WardenProvider {
20
23
  // AuthManager with `strategies: {}` and `defaultStrategy: 'jwt'`,
21
24
  // passing the (then-permissive) constructor and erroring deep in the
22
25
  // middleware loop.
23
- if (!config?.jwt) {
24
- throw new WardenError("WARDEN_NO_AUTH_CONFIG", `@c9up/warden: no authentication strategies configured. Set config.warden.auth.jwt (or another strategy) in your reamrc.ts before registering WardenProvider.`);
26
+ if (!config?.jwt && !config?.session && !config?.apiKey) {
27
+ throw new WardenError("WARDEN_NO_AUTH_CONFIG", `@c9up/warden: no authentication strategies configured. Set at least one of config.warden.auth.jwt / .session / .apiKey in your reamrc.ts before registering WardenProvider.`);
28
+ }
29
+ // Build the strategy table from whatever the app configured. Each strategy
30
+ // is also exposed by its class token (mirrors the jwt convention) so apps
31
+ // can resolve a specific one. The `revoke()` blacklist flows through
32
+ // `config.jwt.blacklist` into JwtStrategy.
33
+ const strategies = {};
34
+ if (config.jwt) {
35
+ const jwt = new JwtStrategy(config.jwt);
36
+ this.app.container.singleton(JwtStrategy, () => jwt);
37
+ strategies.jwt = jwt;
38
+ }
39
+ if (config.session) {
40
+ const session = new SessionStrategy(config.session);
41
+ this.app.container.singleton(SessionStrategy, () => session);
42
+ strategies.session = session;
43
+ }
44
+ if (config.apiKey) {
45
+ const apiKey = new ApiKeyStrategy(config.apiKey);
46
+ this.app.container.singleton(ApiKeyStrategy, () => apiKey);
47
+ strategies["api-key"] = apiKey;
25
48
  }
26
- const jwt = new JwtStrategy(config.jwt);
27
- this.app.container.singleton(JwtStrategy, () => jwt);
28
49
  // Rights layer (Epic 56): one resolver singleton backs BOTH the coarse
29
50
  // RBAC helpers (injected into AuthManager below) and — once 56.6 lands —
30
51
  // the Bouncer construction, so a coarse question and a policy question
@@ -38,10 +59,21 @@ export default class WardenProvider {
38
59
  if (rightsStore instanceof MemoryRightsStore) {
39
60
  this.app.container.singleton(MemoryRightsStore, () => rightsStore);
40
61
  }
62
+ // Bouncer registry (Epic 56.6): the abilities/policies/scope-resolver the
63
+ // per-request `initializeBouncer` middleware builds each `ctx.bouncer`
64
+ // from. Registered (even when empty) under a string token so the agnostic
65
+ // middleware resolves it by name, mirroring the "auth" alias above.
66
+ const bouncerRegistry = {
67
+ abilities: config.abilities ?? {},
68
+ policies: config.policies ?? {},
69
+ resolveScope: config.resolveScope,
70
+ };
71
+ this.app.container.singleton("bouncer:registry", () => bouncerRegistry);
72
+ // Default to the configured strategy, else the first one registered.
73
+ const defaultStrategy = config.defaultStrategy ?? Object.keys(strategies)[0];
41
74
  this.app.container.singleton(AuthManager, () => {
42
- const strategies = { jwt };
43
75
  return new AuthManager({
44
- defaultStrategy: config.defaultStrategy ?? "jwt",
76
+ defaultStrategy,
45
77
  strategies,
46
78
  rights: rightsResolver,
47
79
  });
@@ -53,6 +85,15 @@ export default class WardenProvider {
53
85
  // `container.resolve("auth")` threw and its admin auth gate
54
86
  // silently fell back to open-mode.
55
87
  this.app.container.singleton("auth", () => this.app.container.resolve(AuthManager));
88
+ // MFA (optional): the app builds an MfaManager with its persistent stores
89
+ // + providers and passes it via config.mfa. Registered by class and under
90
+ // the "mfa" string alias (same convention as "auth") so controllers and
91
+ // agnostic consumers can resolve it.
92
+ const mfa = config.mfa?.manager;
93
+ if (mfa) {
94
+ this.app.container.singleton(MfaManager, () => mfa);
95
+ this.app.container.singleton("mfa", () => mfa);
96
+ }
56
97
  }
57
98
  async boot() {
58
99
  setAuth(this.app.container.resolve(AuthManager));
@@ -1 +1 @@
1
- {"version":3,"file":"WardenProvider.js","sourceRoot":"","sources":["../src/WardenProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAgB1D,MAAM,CAAC,OAAO,OAAO,cAAc;IACZ;IAAtB,YAAsB,GAAqB;QAArB,QAAG,GAAH,GAAG,CAAkB;IAAG,CAAC;IAE/C,QAAQ;QACP,4EAA4E;QAC5E,sEAAsE;QACtE,8DAA8D;QAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAe,MAAM,CAAC,CAAC;QAEzD,qEAAqE;QACrE,oEAAoE;QACpE,mEAAmE;QACnE,kEAAkE;QAClE,qEAAqE;QACrE,mBAAmB;QACnB,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;YAClB,MAAM,IAAI,WAAW,CACpB,uBAAuB,EACvB,8JAA8J,CAC9J,CAAC;QACH,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;QAErD,uEAAuE;QACvE,yEAAyE;QACzE,uEAAuE;QACvE,uEAAuE;QACvE,sEAAsE;QACtE,iEAAiE;QACjE,oCAAoC;QACpC,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,IAAI,iBAAiB,EAAE,CAAC;QACpE,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC,WAAW,CAAC,CAAC;QACvD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC;QACnE,IAAI,WAAW,YAAY,iBAAiB,EAAE,CAAC;YAC9C,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,EAAE,GAAG,EAAE;YAC9C,MAAM,UAAU,GAAgC,EAAE,GAAG,EAAE,CAAC;YACxD,OAAO,IAAI,WAAW,CAAC;gBACtB,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,KAAK;gBAChD,UAAU;gBACV,MAAM,EAAE,cAAc;aACtB,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,oEAAoE;QACpE,mEAAmE;QACnE,4DAA4D;QAC5D,8DAA8D;QAC9D,4DAA4D;QAC5D,mCAAmC;QACnC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CACzC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,CACvC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACT,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,CAAgB,CAAC,CAAC;IACjE,CAAC;CACD"}
1
+ {"version":3,"file":"WardenProvider.js","sourceRoot":"","sources":["../src/WardenProvider.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -15,18 +15,55 @@
15
15
  * })
16
16
  */
17
17
  import type { UserPayload } from "./AuthManager.js";
18
- import type { RightsStore } from "./rights/types.js";
18
+ import type { BasePolicy } from "./bouncer/BasePolicy.js";
19
+ import type { Ability } from "./bouncer/types.js";
20
+ import type { MfaManager } from "./mfa/MfaManager.js";
21
+ import type { RightsStore, Scope } from "./rights/types.js";
22
+ import type { ApiKeyConfig } from "./strategies/ApiKeyStrategy.js";
23
+ import type { SessionStrategyConfig } from "./strategies/SessionStrategy.js";
24
+ import type { TokenBlacklist } from "./TokenBlacklist.js";
25
+ /**
26
+ * Minimal request shape passed to `resolveScope` — enough to derive a tenant
27
+ * from a header or the authenticated user, without coupling config to the full
28
+ * HTTP context type (keeps Warden agnostic of the host framework).
29
+ */
30
+ export interface ScopeRequestContext {
31
+ request: {
32
+ headers: Record<string, string>;
33
+ };
34
+ auth?: {
35
+ user?: UserPayload | null;
36
+ };
37
+ }
19
38
  export interface JwtConfig {
20
39
  secret: string;
21
40
  expiresInSeconds?: number;
22
41
  findUser: (id: string) => Promise<UserPayload | null>;
23
42
  verifyCredentials: (email: string, password: string) => Promise<UserPayload | null>;
43
+ /**
44
+ * Optional revocation list. Supply a `TokenBlacklist` (Memory/Redis-backed) to
45
+ * enable `revoke()`/early-rejection of JWTs before expiry. Without it, `revoke()`
46
+ * throws (the call would be a silent no-op).
47
+ */
48
+ blacklist?: TokenBlacklist;
24
49
  }
25
50
  export interface WardenConfig {
26
51
  /** Default auth strategy (default: 'jwt'). */
27
52
  defaultStrategy?: string;
28
53
  /** JWT strategy configuration. */
29
54
  jwt?: JwtConfig;
55
+ /**
56
+ * Session strategy configuration. Supply `findUser` so `@Guard('session')`
57
+ * routes resolve the authenticated user from the session id. Without this,
58
+ * the `session` strategy is unregistered and `@Guard('session')` throws.
59
+ */
60
+ session?: SessionStrategyConfig;
61
+ /**
62
+ * API-key strategy configuration. Supply `findByKey` so `@Guard('api-key')`
63
+ * routes resolve the user (and scopes) from the request header. Without this,
64
+ * the `api-key` strategy is unregistered and `@Guard('api-key')` throws.
65
+ */
66
+ apiKey?: ApiKeyConfig;
30
67
  /**
31
68
  * Rights layer configuration (Epic 56). Supply a custom `store` to back the
32
69
  * unified resolver with a DB-backed driver; when omitted, an in-memory
@@ -36,6 +73,33 @@ export interface WardenConfig {
36
73
  rights?: {
37
74
  store?: RightsStore;
38
75
  };
76
+ /**
77
+ * Standalone abilities the per-request Bouncer knows by name (Epic 56.6).
78
+ * Keyed by the string passed to `ctx.bouncer.authorize('post.edit', …)`.
79
+ * Define with `Bouncer.ability((user, post) => …)`.
80
+ */
81
+ abilities?: Record<string, Ability<never[]>>;
82
+ /**
83
+ * Class-based policies the per-request Bouncer knows by name (Epic 56.6).
84
+ * Reachable via `ctx.bouncer.with('PostPolicy')`. Each entry is the policy
85
+ * constructor; a fresh instance is created per check.
86
+ */
87
+ policies?: Record<string, new () => BasePolicy>;
88
+ /**
89
+ * Derive the authorization scope for a request (AD4 — scope-first). Return
90
+ * `{ tenant }` to scope rights to a tenant (e.g. from a subdomain or header),
91
+ * or `"global"`. Omitted ⇒ every request runs in the implicit `global` scope
92
+ * (zero config for single-tenant apps).
93
+ */
94
+ resolveScope?: (ctx: ScopeRequestContext) => Scope | Promise<Scope>;
95
+ /**
96
+ * Multi-factor authentication. Supply a configured `MfaManager` (built with
97
+ * your persistent stores + providers) to register it in the container as
98
+ * `MfaManager` / `"mfa"`. Required to use `@RequireMfa` step-up flows.
99
+ */
100
+ mfa?: {
101
+ manager: MfaManager;
102
+ };
39
103
  }
40
104
  /** Typed config helper — identity function for editor inference. */
41
105
  export declare function defineConfig(config: WardenConfig): WardenConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,SAAS;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtD,iBAAiB,EAAE,CAClB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACZ,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC5B,8CAA8C;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kCAAkC;IAClC,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,WAAW,CAAA;KAAE,CAAC;CACjC;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAE/D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC7C,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,CAAC;CACrC;AAED,MAAM,WAAW,SAAS;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtD,iBAAiB,EAAE,CAClB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,KACZ,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC5B,8CAA8C;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kCAAkC;IAClC,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,WAAW,CAAA;KAAE,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,UAAU,CAAC,CAAC;IAChD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACpE;;;;OAIG;IACH,GAAG,CAAC,EAAE;QAAE,OAAO,EAAE,UAAU,CAAA;KAAE,CAAC;CAC9B;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAE/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA6BH,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAAC,MAAoB;IAChD,OAAO,MAAM,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAwFH,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAAC,MAAoB;IAChD,OAAO,MAAM,CAAC;AACf,CAAC"}
package/dist/index.d.ts CHANGED
@@ -20,9 +20,23 @@ export { GitHubDriver } from "./firstcontact/drivers/GitHubDriver.js";
20
20
  export { GoogleDriver } from "./firstcontact/drivers/GoogleDriver.js";
21
21
  export { FirstContactManager } from "./firstcontact/FirstContactManager.js";
22
22
  export type { FirstContactDriver, OAuthConfig, OAuthToken, OAuthUser, } from "./firstcontact/types.js";
23
- export { Guard, getGuardMetadata, getPermissionMetadata, getRoleMetadata, Permission, Role, } from "./Guard.js";
23
+ export { Guard, getGuardMetadata, getPermissionMetadata, getRequireMfaMetadata, getRoleMetadata, Permission, RequireMfa, Role, } from "./Guard.js";
24
+ export type { BackupCodesConfig, BackupCodesResult, BackupCodeVerification, } from "./mfa/BackupCodesProvider.js";
25
+ export { BackupCodesProvider } from "./mfa/BackupCodesProvider.js";
26
+ export type { MfaFactor, MfaFactorKind, MfaFactorStore, MfaFactorSummary, MfaManagerConfig, } from "./mfa/MfaManager.js";
27
+ export { MemoryMfaFactorStore, MfaManager } from "./mfa/MfaManager.js";
28
+ export type { OtpChallenge, OtpChallengeStore, OtpConfig, OtpDeliveryChannel, OtpFailureReason, OtpStartResult, OtpVerification, } from "./mfa/OtpProvider.js";
29
+ export { MemoryOtpChallengeStore, OtpProvider } from "./mfa/OtpProvider.js";
30
+ export type { TotpAlgorithm, TotpConfig, TotpEnrollment, } from "./mfa/TotpProvider.js";
31
+ export { TotpProvider } from "./mfa/TotpProvider.js";
32
+ export type { AuthenticationOptionsJSON, AuthenticationResponseJSON, RegistrationOptionsJSON, RegistrationResponseJSON, StoredPasskey, WebauthnChallengeStore, WebauthnConfig, WebauthnCredentialStore, WebauthnUser, } from "./mfa/WebauthnProvider.js";
33
+ export { MemoryWebauthnChallengeStore, MemoryWebauthnCredentialStore, WebauthnProvider, } from "./mfa/WebauthnProvider.js";
34
+ export type { RedisBlacklistConfig, RedisLikeClient, } from "./RedisBlacklistDriver.js";
35
+ export { RedisBlacklistDriver } from "./RedisBlacklistDriver.js";
24
36
  export type { RefreshTokenDriver, StoredRefreshToken, } from "./RefreshTokenStore.js";
25
37
  export { generateRefreshToken, MemoryRefreshTokenDriver, } from "./RefreshTokenStore.js";
38
+ export type { BlacklistDegradeEvent, ResilientBlacklistConfig, } from "./ResilientBlacklistDriver.js";
39
+ export { ResilientBlacklistDriver } from "./ResilientBlacklistDriver.js";
26
40
  export { MemoryRightsStore } from "./rights/MemoryRightsStore.js";
27
41
  export { RightsResolver } from "./rights/RightsResolver.js";
28
42
  export type { EffectivePermissions, RightsStore, Scope, } from "./rights/types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACX,UAAU,EACV,UAAU,EACV,YAAY,EACZ,WAAW,GACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EACN,MAAM,EACN,UAAU,EACV,iBAAiB,GACjB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EACX,OAAO,EACP,cAAc,EACd,kBAAkB,EAClB,cAAc,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAC5E,YAAY,EACX,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,SAAS,GACT,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,KAAK,EACL,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,IAAI,GACJ,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,oBAAoB,EACpB,wBAAwB,GACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,YAAY,EACX,oBAAoB,EACpB,WAAW,EACX,KAAK,GACL,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC7E,YAAY,EACX,YAAY,EACZ,qBAAqB,GACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}