@c9up/warden 0.1.3 → 0.1.5

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 (185) hide show
  1. package/README.md +57 -0
  2. package/dist/AuthManager.d.ts +98 -0
  3. package/dist/AuthManager.d.ts.map +1 -0
  4. package/dist/AuthManager.js +147 -0
  5. package/dist/AuthManager.js.map +1 -0
  6. package/dist/AuthRateLimiter.d.ts +29 -0
  7. package/dist/AuthRateLimiter.d.ts.map +1 -0
  8. package/dist/AuthRateLimiter.js +88 -0
  9. package/dist/AuthRateLimiter.js.map +1 -0
  10. package/dist/Guard.d.ts +35 -0
  11. package/dist/Guard.d.ts.map +1 -0
  12. package/dist/Guard.js +68 -0
  13. package/dist/Guard.js.map +1 -0
  14. package/dist/RedisBlacklistDriver.d.ts +32 -0
  15. package/dist/RedisBlacklistDriver.d.ts.map +1 -0
  16. package/dist/RedisBlacklistDriver.js +37 -0
  17. package/dist/RedisBlacklistDriver.js.map +1 -0
  18. package/dist/RefreshTokenStore.d.ts +34 -0
  19. package/dist/RefreshTokenStore.d.ts.map +1 -0
  20. package/dist/RefreshTokenStore.js +48 -0
  21. package/dist/RefreshTokenStore.js.map +1 -0
  22. package/dist/ResilientBlacklistDriver.d.ts +35 -0
  23. package/dist/ResilientBlacklistDriver.d.ts.map +1 -0
  24. package/dist/ResilientBlacklistDriver.js +61 -0
  25. package/dist/ResilientBlacklistDriver.js.map +1 -0
  26. package/dist/TokenBlacklist.d.ts +30 -0
  27. package/dist/TokenBlacklist.d.ts.map +1 -0
  28. package/dist/TokenBlacklist.js +50 -0
  29. package/dist/TokenBlacklist.js.map +1 -0
  30. package/dist/WardenProvider.d.ts +19 -0
  31. package/dist/WardenProvider.d.ts.map +1 -0
  32. package/dist/WardenProvider.js +71 -0
  33. package/dist/WardenProvider.js.map +1 -0
  34. package/dist/bouncer/AuthorizationResponse.d.ts +25 -0
  35. package/dist/bouncer/AuthorizationResponse.d.ts.map +1 -0
  36. package/dist/bouncer/AuthorizationResponse.js +31 -0
  37. package/dist/bouncer/AuthorizationResponse.js.map +1 -0
  38. package/dist/bouncer/BasePolicy.d.ts +47 -0
  39. package/dist/bouncer/BasePolicy.d.ts.map +1 -0
  40. package/dist/bouncer/BasePolicy.js +36 -0
  41. package/dist/bouncer/BasePolicy.js.map +1 -0
  42. package/dist/bouncer/Bouncer.d.ts +47 -0
  43. package/dist/bouncer/Bouncer.d.ts.map +1 -0
  44. package/dist/bouncer/Bouncer.js +134 -0
  45. package/dist/bouncer/Bouncer.js.map +1 -0
  46. package/dist/bouncer/PolicyAuthorizer.d.ts +24 -0
  47. package/dist/bouncer/PolicyAuthorizer.d.ts.map +1 -0
  48. package/dist/bouncer/PolicyAuthorizer.js +92 -0
  49. package/dist/bouncer/PolicyAuthorizer.js.map +1 -0
  50. package/dist/bouncer/decorators.d.ts +25 -0
  51. package/dist/bouncer/decorators.d.ts.map +1 -0
  52. package/dist/bouncer/decorators.js +36 -0
  53. package/dist/bouncer/decorators.js.map +1 -0
  54. package/dist/bouncer/evaluate.d.ts +36 -0
  55. package/dist/bouncer/evaluate.d.ts.map +1 -0
  56. package/dist/bouncer/evaluate.js +64 -0
  57. package/dist/bouncer/evaluate.js.map +1 -0
  58. package/dist/bouncer/policyContext.d.ts +29 -0
  59. package/dist/bouncer/policyContext.d.ts.map +1 -0
  60. package/dist/bouncer/policyContext.js +36 -0
  61. package/dist/bouncer/policyContext.js.map +1 -0
  62. package/dist/bouncer/types.d.ts +52 -0
  63. package/dist/bouncer/types.d.ts.map +1 -0
  64. package/dist/bouncer/types.js +9 -0
  65. package/dist/bouncer/types.js.map +1 -0
  66. package/dist/config.d.ts +51 -0
  67. package/dist/config.d.ts.map +1 -0
  68. package/dist/config.js +21 -0
  69. package/dist/config.js.map +1 -0
  70. package/dist/configure.d.ts +10 -0
  71. package/dist/configure.d.ts.map +1 -0
  72. package/dist/configure.js +44 -0
  73. package/dist/configure.js.map +1 -0
  74. package/dist/errors.d.ts +18 -0
  75. package/dist/errors.d.ts.map +1 -0
  76. package/dist/errors.js +21 -0
  77. package/dist/errors.js.map +1 -0
  78. package/dist/firstcontact/FirstContactManager.d.ts +25 -0
  79. package/dist/firstcontact/FirstContactManager.d.ts.map +1 -0
  80. package/dist/firstcontact/FirstContactManager.js +38 -0
  81. package/dist/firstcontact/FirstContactManager.js.map +1 -0
  82. package/dist/firstcontact/drivers/GitHubDriver.d.ts +14 -0
  83. package/dist/firstcontact/drivers/GitHubDriver.d.ts.map +1 -0
  84. package/dist/firstcontact/drivers/GitHubDriver.js +61 -0
  85. package/dist/firstcontact/drivers/GitHubDriver.js.map +1 -0
  86. package/dist/firstcontact/drivers/GoogleDriver.d.ts +14 -0
  87. package/dist/firstcontact/drivers/GoogleDriver.d.ts.map +1 -0
  88. package/dist/firstcontact/drivers/GoogleDriver.js +64 -0
  89. package/dist/firstcontact/drivers/GoogleDriver.js.map +1 -0
  90. package/dist/firstcontact/types.d.ts +34 -0
  91. package/dist/firstcontact/types.d.ts.map +1 -0
  92. package/dist/firstcontact/types.js +5 -0
  93. package/dist/firstcontact/types.js.map +1 -0
  94. package/dist/index.d.ts +52 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +34 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/mfa/BackupCodesProvider.d.ts +39 -0
  99. package/dist/mfa/BackupCodesProvider.d.ts.map +1 -0
  100. package/dist/mfa/BackupCodesProvider.js +87 -0
  101. package/dist/mfa/BackupCodesProvider.js.map +1 -0
  102. package/dist/mfa/MfaManager.d.ts +107 -0
  103. package/dist/mfa/MfaManager.d.ts.map +1 -0
  104. package/dist/mfa/MfaManager.js +219 -0
  105. package/dist/mfa/MfaManager.js.map +1 -0
  106. package/dist/mfa/OtpProvider.d.ts +67 -0
  107. package/dist/mfa/OtpProvider.d.ts.map +1 -0
  108. package/dist/mfa/OtpProvider.js +104 -0
  109. package/dist/mfa/OtpProvider.js.map +1 -0
  110. package/dist/mfa/TotpProvider.d.ts +52 -0
  111. package/dist/mfa/TotpProvider.d.ts.map +1 -0
  112. package/dist/mfa/TotpProvider.js +103 -0
  113. package/dist/mfa/TotpProvider.js.map +1 -0
  114. package/dist/mfa/WebauthnProvider.d.ts +175 -0
  115. package/dist/mfa/WebauthnProvider.d.ts.map +1 -0
  116. package/dist/mfa/WebauthnProvider.js +239 -0
  117. package/dist/mfa/WebauthnProvider.js.map +1 -0
  118. package/dist/mfa/base32.d.ts +13 -0
  119. package/dist/mfa/base32.d.ts.map +1 -0
  120. package/dist/mfa/base32.js +52 -0
  121. package/dist/mfa/base32.js.map +1 -0
  122. package/dist/mfa/webauthn-codec.d.ts +58 -0
  123. package/dist/mfa/webauthn-codec.d.ts.map +1 -0
  124. package/dist/mfa/webauthn-codec.js +221 -0
  125. package/dist/mfa/webauthn-codec.js.map +1 -0
  126. package/dist/middleware.d.ts +51 -0
  127. package/dist/middleware.d.ts.map +1 -0
  128. package/dist/middleware.js +213 -0
  129. package/dist/middleware.js.map +1 -0
  130. package/dist/native.d.ts +14 -0
  131. package/dist/native.d.ts.map +1 -0
  132. package/dist/native.js +39 -0
  133. package/dist/native.js.map +1 -0
  134. package/dist/rights/MemoryRightsStore.d.ts +29 -0
  135. package/dist/rights/MemoryRightsStore.d.ts.map +1 -0
  136. package/dist/rights/MemoryRightsStore.js +70 -0
  137. package/dist/rights/MemoryRightsStore.js.map +1 -0
  138. package/dist/rights/RightsResolver.d.ts +29 -0
  139. package/dist/rights/RightsResolver.d.ts.map +1 -0
  140. package/dist/rights/RightsResolver.js +73 -0
  141. package/dist/rights/RightsResolver.js.map +1 -0
  142. package/dist/rights/types.d.ts +55 -0
  143. package/dist/rights/types.d.ts.map +1 -0
  144. package/dist/rights/types.js +18 -0
  145. package/dist/rights/types.js.map +1 -0
  146. package/dist/services/main.d.ts +19 -0
  147. package/dist/services/main.d.ts.map +1 -0
  148. package/dist/services/main.js +32 -0
  149. package/dist/services/main.js.map +1 -0
  150. package/dist/standalone.d.ts +37 -0
  151. package/dist/standalone.d.ts.map +1 -0
  152. package/dist/standalone.js +92 -0
  153. package/dist/standalone.js.map +1 -0
  154. package/dist/strategies/ApiKeyStrategy.d.ts +28 -0
  155. package/dist/strategies/ApiKeyStrategy.d.ts.map +1 -0
  156. package/dist/strategies/ApiKeyStrategy.js +36 -0
  157. package/dist/strategies/ApiKeyStrategy.js.map +1 -0
  158. package/dist/strategies/JwtStrategy.d.ts +51 -0
  159. package/dist/strategies/JwtStrategy.d.ts.map +1 -0
  160. package/dist/strategies/JwtStrategy.js +150 -0
  161. package/dist/strategies/JwtStrategy.js.map +1 -0
  162. package/dist/strategies/SessionStrategy.d.ts +58 -0
  163. package/dist/strategies/SessionStrategy.d.ts.map +1 -0
  164. package/dist/strategies/SessionStrategy.js +70 -0
  165. package/dist/strategies/SessionStrategy.js.map +1 -0
  166. package/index.darwin-arm64.node +0 -0
  167. package/index.darwin-x64.node +0 -0
  168. package/index.linux-arm64-gnu.node +0 -0
  169. package/index.linux-x64-gnu.node +0 -0
  170. package/index.win32-x64-msvc.node +0 -0
  171. package/package.json +1 -1
  172. package/src/Guard.ts +21 -0
  173. package/src/RedisBlacklistDriver.ts +60 -0
  174. package/src/ResilientBlacklistDriver.ts +84 -0
  175. package/src/WardenProvider.ts +11 -0
  176. package/src/config.ts +7 -0
  177. package/src/index.ts +58 -0
  178. package/src/mfa/BackupCodesProvider.ts +125 -0
  179. package/src/mfa/MfaManager.ts +307 -0
  180. package/src/mfa/OtpProvider.ts +177 -0
  181. package/src/mfa/TotpProvider.ts +140 -0
  182. package/src/mfa/WebauthnProvider.ts +416 -0
  183. package/src/mfa/base32.ts +54 -0
  184. package/src/mfa/webauthn-codec.ts +264 -0
  185. package/src/middleware.ts +14 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * RefreshTokenStore — server-side opaque refresh token management.
3
+ *
4
+ * AdonisJS opaque token pattern:
5
+ * { accessToken, refreshToken, expiresIn }
6
+ * POST /auth/refresh → new pair, old token invalidated
7
+ *
8
+ * @implements MISS-5
9
+ */
10
+ export interface StoredRefreshToken {
11
+ token: string;
12
+ userId: string | number;
13
+ expiresAt: number;
14
+ createdAt: number;
15
+ }
16
+ export interface RefreshTokenDriver {
17
+ store(token: StoredRefreshToken): Promise<void>;
18
+ find(token: string): Promise<StoredRefreshToken | null>;
19
+ revoke(token: string): Promise<void>;
20
+ revokeAllForUser(userId: string | number): Promise<void>;
21
+ cleanup(): Promise<void>;
22
+ }
23
+ /** In-memory driver for development. */
24
+ export declare class MemoryRefreshTokenDriver implements RefreshTokenDriver {
25
+ private tokens;
26
+ store(token: StoredRefreshToken): Promise<void>;
27
+ find(token: string): Promise<StoredRefreshToken | null>;
28
+ revoke(token: string): Promise<void>;
29
+ revokeAllForUser(userId: string | number): Promise<void>;
30
+ cleanup(): Promise<void>;
31
+ }
32
+ /** Generate an opaque refresh token. */
33
+ export declare function generateRefreshToken(): string;
34
+ //# sourceMappingURL=RefreshTokenStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RefreshTokenStore.d.ts","sourceRoot":"","sources":["../src/RefreshTokenStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED,wCAAwC;AACxC,qBAAa,wBAAyB,YAAW,kBAAkB;IAClE,OAAO,CAAC,MAAM,CAA8C;IAEtD,KAAK,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAUvD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAM9B;AAED,wCAAwC;AACxC,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * RefreshTokenStore — server-side opaque refresh token management.
3
+ *
4
+ * AdonisJS opaque token pattern:
5
+ * { accessToken, refreshToken, expiresIn }
6
+ * POST /auth/refresh → new pair, old token invalidated
7
+ *
8
+ * @implements MISS-5
9
+ */
10
+ import { randomBytes } from "node:crypto";
11
+ /** In-memory driver for development. */
12
+ export class MemoryRefreshTokenDriver {
13
+ tokens = new Map();
14
+ async store(token) {
15
+ this.tokens.set(token.token, token);
16
+ }
17
+ async find(token) {
18
+ const stored = this.tokens.get(token);
19
+ if (!stored)
20
+ return null;
21
+ if (stored.expiresAt < Date.now()) {
22
+ this.tokens.delete(token);
23
+ return null;
24
+ }
25
+ return stored;
26
+ }
27
+ async revoke(token) {
28
+ this.tokens.delete(token);
29
+ }
30
+ async revokeAllForUser(userId) {
31
+ for (const [key, val] of this.tokens) {
32
+ if (val.userId === userId)
33
+ this.tokens.delete(key);
34
+ }
35
+ }
36
+ async cleanup() {
37
+ const now = Date.now();
38
+ for (const [key, val] of this.tokens) {
39
+ if (val.expiresAt < now)
40
+ this.tokens.delete(key);
41
+ }
42
+ }
43
+ }
44
+ /** Generate an opaque refresh token. */
45
+ export function generateRefreshToken() {
46
+ return randomBytes(48).toString("base64url");
47
+ }
48
+ //# sourceMappingURL=RefreshTokenStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RefreshTokenStore.js","sourceRoot":"","sources":["../src/RefreshTokenStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAiB1C,wCAAwC;AACxC,MAAM,OAAO,wBAAwB;IAC5B,MAAM,GAAoC,IAAI,GAAG,EAAE,CAAC;IAE5D,KAAK,CAAC,KAAK,CAAC,KAAyB;QACpC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAa;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YACnC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC1B,OAAO,IAAI,CAAC;QACb,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa;QACzB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,MAAuB;QAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACtC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACpD,CAAC;IACF,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,MAAM,EAAE,CAAC;YACtC,IAAI,GAAG,CAAC,SAAS,GAAG,GAAG;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC;IACF,CAAC;CACD;AAED,wCAAwC;AACxC,MAAM,UAAU,oBAAoB;IACnC,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC9C,CAAC"}
@@ -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,CAAC;QAC/B,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,CAAC;IAChC,CAAC;CACD"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * TokenBlacklist — revoke JWT tokens before expiry.
3
+ *
4
+ * Stores JTI (JWT ID) with expiry. Supports Memory and Redis drivers.
5
+ *
6
+ * @implements MISS-6
7
+ */
8
+ export interface BlacklistDriver {
9
+ add(jti: string, expiresAt: number): Promise<void>;
10
+ has(jti: string): Promise<boolean>;
11
+ cleanup(): Promise<void>;
12
+ }
13
+ /** In-memory blacklist for development. */
14
+ export declare class MemoryBlacklistDriver implements BlacklistDriver {
15
+ private entries;
16
+ add(jti: string, expiresAt: number): Promise<void>;
17
+ has(jti: string): Promise<boolean>;
18
+ cleanup(): Promise<void>;
19
+ }
20
+ export declare class TokenBlacklist {
21
+ private driver;
22
+ constructor(driver: BlacklistDriver);
23
+ /** Revoke a token by its JTI claim. */
24
+ revoke(jti: string, expiresAt: number): Promise<void>;
25
+ /** Check if a token JTI is blacklisted. */
26
+ isRevoked(jti: string): Promise<boolean>;
27
+ /** Remove expired entries. */
28
+ cleanup(): Promise<void>;
29
+ }
30
+ //# sourceMappingURL=TokenBlacklist.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * TokenBlacklist — revoke JWT tokens before expiry.
3
+ *
4
+ * Stores JTI (JWT ID) with expiry. Supports Memory and Redis drivers.
5
+ *
6
+ * @implements MISS-6
7
+ */
8
+ /** In-memory blacklist for development. */
9
+ export class MemoryBlacklistDriver {
10
+ entries = new Map();
11
+ async add(jti, expiresAt) {
12
+ this.entries.set(jti, expiresAt);
13
+ }
14
+ async has(jti) {
15
+ const expiresAt = this.entries.get(jti);
16
+ if (expiresAt === undefined)
17
+ return false;
18
+ if (expiresAt < Date.now()) {
19
+ this.entries.delete(jti);
20
+ return false;
21
+ }
22
+ return true;
23
+ }
24
+ async cleanup() {
25
+ const now = Date.now();
26
+ for (const [jti, exp] of this.entries) {
27
+ if (exp < now)
28
+ this.entries.delete(jti);
29
+ }
30
+ }
31
+ }
32
+ export class TokenBlacklist {
33
+ driver;
34
+ constructor(driver) {
35
+ this.driver = driver;
36
+ }
37
+ /** Revoke a token by its JTI claim. */
38
+ async revoke(jti, expiresAt) {
39
+ await this.driver.add(jti, expiresAt);
40
+ }
41
+ /** Check if a token JTI is blacklisted. */
42
+ async isRevoked(jti) {
43
+ return this.driver.has(jti);
44
+ }
45
+ /** Remove expired entries. */
46
+ async cleanup() {
47
+ return this.driver.cleanup();
48
+ }
49
+ }
50
+ //# sourceMappingURL=TokenBlacklist.js.map
@@ -0,0 +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"}
@@ -0,0 +1,19 @@
1
+ interface WardenContainer {
2
+ singleton(token: unknown, factory: () => unknown): void;
3
+ resolve(token: unknown): unknown;
4
+ }
5
+ interface WardenConfigStore {
6
+ get<T = unknown>(key: string): T | undefined;
7
+ }
8
+ export interface WardenAppContext {
9
+ container: WardenContainer;
10
+ config: WardenConfigStore;
11
+ }
12
+ export default class WardenProvider {
13
+ protected app: WardenAppContext;
14
+ constructor(app: WardenAppContext);
15
+ register(): void;
16
+ boot(): Promise<void>;
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=WardenProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WardenProvider.d.ts","sourceRoot":"","sources":["../src/WardenProvider.ts"],"names":[],"mappings":"AASA,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;IAiEF,IAAI;CAGV"}
@@ -0,0 +1,71 @@
1
+ import { AuthManager } from "./AuthManager.js";
2
+ import { WardenError } from "./errors.js";
3
+ import { MfaManager } from "./mfa/MfaManager.js";
4
+ import { MemoryRightsStore } from "./rights/MemoryRightsStore.js";
5
+ import { RightsResolver } from "./rights/RightsResolver.js";
6
+ import { setAuth } from "./services/main.js";
7
+ import { JwtStrategy } from "./strategies/JwtStrategy.js";
8
+ export default class WardenProvider {
9
+ app;
10
+ constructor(app) {
11
+ this.app = app;
12
+ }
13
+ register() {
14
+ // Register JwtStrategy first so it's available before AuthManager resolves.
15
+ // The previous version nested the JwtStrategy registration inside the
16
+ // AuthManager factory — that was order-dependent and fragile.
17
+ const config = this.app.config.get("auth");
18
+ // Fail-fast at register-time so the operator gets a clear actionable
19
+ // error during boot instead of an opaque runtime crash on the first
20
+ // protected request. Previously, missing `config.auth` produced an
21
+ // AuthManager with `strategies: {}` and `defaultStrategy: 'jwt'`,
22
+ // passing the (then-permissive) constructor and erroring deep in the
23
+ // middleware loop.
24
+ if (!config?.jwt) {
25
+ 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
+ }
27
+ const jwt = new JwtStrategy(config.jwt);
28
+ this.app.container.singleton(JwtStrategy, () => jwt);
29
+ // Rights layer (Epic 56): one resolver singleton backs BOTH the coarse
30
+ // RBAC helpers (injected into AuthManager below) and — once 56.6 lands —
31
+ // the Bouncer construction, so a coarse question and a policy question
32
+ // resolve through the SAME instance (single unification point, AC-E3).
33
+ // The store defaults to an in-memory driver and is exposed (by class)
34
+ // so an app can seed roles/grants at boot, or supply its own via
35
+ // `config.auth.rights.store` (AD5).
36
+ const rightsStore = config.rights?.store ?? new MemoryRightsStore();
37
+ const rightsResolver = new RightsResolver(rightsStore);
38
+ this.app.container.singleton(RightsResolver, () => rightsResolver);
39
+ if (rightsStore instanceof MemoryRightsStore) {
40
+ this.app.container.singleton(MemoryRightsStore, () => rightsStore);
41
+ }
42
+ this.app.container.singleton(AuthManager, () => {
43
+ const strategies = { jwt };
44
+ return new AuthManager({
45
+ defaultStrategy: config.defaultStrategy ?? "jwt",
46
+ strategies,
47
+ rights: rightsResolver,
48
+ });
49
+ });
50
+ // String alias so consumers that can't import the AuthManager class
51
+ // (e.g. @c9up/station, which stays agnostic of warden) can resolve
52
+ // it by name. Mirrors the convention other providers follow
53
+ // (events → "bus", rosetta → "i18n"). Without this, Station's
54
+ // `container.resolve("auth")` threw and its admin auth gate
55
+ // silently fell back to open-mode.
56
+ this.app.container.singleton("auth", () => this.app.container.resolve(AuthManager));
57
+ // MFA (optional): the app builds an MfaManager with its persistent stores
58
+ // + providers and passes it via config.mfa. Registered by class and under
59
+ // the "mfa" string alias (same convention as "auth") so controllers and
60
+ // agnostic consumers can resolve it.
61
+ const mfa = config.mfa?.manager;
62
+ if (mfa) {
63
+ this.app.container.singleton(MfaManager, () => mfa);
64
+ this.app.container.singleton("mfa", () => mfa);
65
+ }
66
+ }
67
+ async boot() {
68
+ setAuth(this.app.container.resolve(AuthManager));
69
+ }
70
+ }
71
+ //# sourceMappingURL=WardenProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WardenProvider.js","sourceRoot":"","sources":["../src/WardenProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * AuthorizationResponse — the value type every Bouncer check resolves to.
3
+ *
4
+ * Layer 2 (EVALUATION) of Warden's unified authorization (Epic 56). Faithful
5
+ * to AdonisJS Bouncer's `AuthorizationResponse`: instances are produced only by
6
+ * the `allow()` / `deny()` static constructors (the constructor is private), a
7
+ * denial defaults to HTTP 403, and a `translation` field is carried for shape
8
+ * parity with Adonis's i18n hook (always `undefined` in 56.2 — D6).
9
+ */
10
+ export declare class AuthorizationResponse {
11
+ readonly authorized: boolean;
12
+ readonly message?: string;
13
+ readonly status?: number;
14
+ /** Parity placeholder for an i18n binding (always undefined in 56.2 — D6). */
15
+ readonly translation?: {
16
+ identifier: string;
17
+ data?: Record<string, unknown>;
18
+ };
19
+ private constructor();
20
+ /** Authorized response (no status). */
21
+ static allow(): AuthorizationResponse;
22
+ /** Denied response; `status` defaults to 403 (D6). */
23
+ static deny(message?: string, status?: number): AuthorizationResponse;
24
+ }
25
+ //# sourceMappingURL=AuthorizationResponse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthorizationResponse.d.ts","sourceRoot":"","sources":["../../src/bouncer/AuthorizationResponse.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,qBAAa,qBAAqB;IACjC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;IAE9E,OAAO;IAYP,uCAAuC;IACvC,MAAM,CAAC,KAAK,IAAI,qBAAqB;IAIrC,sDAAsD;IACtD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,qBAAqB;CAGlE"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * AuthorizationResponse — the value type every Bouncer check resolves to.
3
+ *
4
+ * Layer 2 (EVALUATION) of Warden's unified authorization (Epic 56). Faithful
5
+ * to AdonisJS Bouncer's `AuthorizationResponse`: instances are produced only by
6
+ * the `allow()` / `deny()` static constructors (the constructor is private), a
7
+ * denial defaults to HTTP 403, and a `translation` field is carried for shape
8
+ * parity with Adonis's i18n hook (always `undefined` in 56.2 — D6).
9
+ */
10
+ export class AuthorizationResponse {
11
+ authorized;
12
+ message;
13
+ status;
14
+ /** Parity placeholder for an i18n binding (always undefined in 56.2 — D6). */
15
+ translation;
16
+ constructor(authorized, message, status, translation) {
17
+ this.authorized = authorized;
18
+ this.message = message;
19
+ this.status = status;
20
+ this.translation = translation;
21
+ }
22
+ /** Authorized response (no status). */
23
+ static allow() {
24
+ return new AuthorizationResponse(true);
25
+ }
26
+ /** Denied response; `status` defaults to 403 (D6). */
27
+ static deny(message, status = 403) {
28
+ return new AuthorizationResponse(false, message, status);
29
+ }
30
+ }
31
+ //# sourceMappingURL=AuthorizationResponse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthorizationResponse.js","sourceRoot":"","sources":["../../src/bouncer/AuthorizationResponse.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,OAAO,qBAAqB;IACxB,UAAU,CAAU;IACpB,OAAO,CAAU;IACjB,MAAM,CAAU;IACzB,8EAA8E;IACrE,WAAW,CAA0D;IAE9E,YACC,UAAmB,EACnB,OAAgB,EAChB,MAAe,EACf,WAAoE;QAEpE,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IAChC,CAAC;IAED,uCAAuC;IACvC,MAAM,CAAC,KAAK;QACX,OAAO,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IAED,sDAAsD;IACtD,MAAM,CAAC,IAAI,CAAC,OAAgB,EAAE,MAAM,GAAG,GAAG;QACzC,OAAO,IAAI,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1D,CAAC;CACD"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * BasePolicy — extend it to group authorization checks for a resource. Action
3
+ * methods are positional `(user, resource?) => AuthorizerResponse` (D3); decorate
4
+ * a method with `@allowGuest()` / `@action({ allowGuest: true })` to let a guest
5
+ * (null user) reach it. The optional `before` / `after` hooks run around every
6
+ * action in the Adonis evaluation order (D5 — see PolicyAuthorizer).
7
+ */
8
+ import type { UserPayload } from "../AuthManager.js";
9
+ import type { EffectivePermissions, Scope } from "../rights/types.js";
10
+ import type { AuthorizationResponse } from "./AuthorizationResponse.js";
11
+ import type { HookResponse } from "./types.js";
12
+ export declare abstract class BasePolicy {
13
+ /**
14
+ * Runs before the action. A non-`undefined` return short-circuits the action
15
+ * (a `boolean`/`AuthorizationResponse` becomes the result) — this is how a
16
+ * moderator bypass or an early `deny('not found', 404)` works, including for
17
+ * a guest. `undefined`/`void` falls through to the action.
18
+ */
19
+ before?(user: UserPayload | null, action: string, ...args: unknown[]): HookResponse;
20
+ /**
21
+ * Runs after the action (or after a `before` short-circuit), receiving the
22
+ * resolved response. A non-`undefined` return overrides it; `undefined`
23
+ * keeps it.
24
+ */
25
+ after?(user: UserPayload | null, action: string, result: AuthorizationResponse): HookResponse;
26
+ /**
27
+ * The Bouncer's active scope for the in-flight check (56.3). Defaults to
28
+ * `"global"` when the policy is used without a Bouncer context.
29
+ */
30
+ protected get scope(): Scope;
31
+ /**
32
+ * The resolved `EffectivePermissions` for the Bouncer's `(user, scope)` (56.3):
33
+ * role-derived ∪ ACL grants with global→tenant inheritance (per 56.1). A guest,
34
+ * a no-resolver Bouncer, or a standalone policy ⇒ empty permissions.
35
+ */
36
+ protected get permissions(): EffectivePermissions;
37
+ /**
38
+ * Tenant-isolation helper (D5 — enforceable, not automatic). `true` under the
39
+ * `global` scope (single-tenant — no boundary); under `{ tenant: T }` it is
40
+ * `resource.tenantId === T`. A policy enforces explicitly, e.g.
41
+ * `if (!this.sameTenant(post)) return false`.
42
+ */
43
+ protected sameTenant(resource: {
44
+ tenantId?: string | null;
45
+ }): boolean;
46
+ }
47
+ //# sourceMappingURL=BasePolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BasePolicy.d.ts","sourceRoot":"","sources":["../../src/bouncer/BasePolicy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,8BAAsB,UAAU;IAC/B;;;;;OAKG;IACH,MAAM,CAAC,CACN,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,OAAO,EAAE,GAChB,YAAY;IAEf;;;;OAIG;IACH,KAAK,CAAC,CACL,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,qBAAqB,GAC3B,YAAY;IAEf;;;OAGG;IACH,SAAS,KAAK,KAAK,IAAI,KAAK,CAE3B;IAED;;;;OAIG;IACH,SAAS,KAAK,WAAW,IAAI,oBAAoB,CAEhD;IAED;;;;;OAKG;IACH,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO;CAIrE"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * BasePolicy — extend it to group authorization checks for a resource. Action
3
+ * methods are positional `(user, resource?) => AuthorizerResponse` (D3); decorate
4
+ * a method with `@allowGuest()` / `@action({ allowGuest: true })` to let a guest
5
+ * (null user) reach it. The optional `before` / `after` hooks run around every
6
+ * action in the Adonis evaluation order (D5 — see PolicyAuthorizer).
7
+ */
8
+ import { emptyPermissions, getPolicyContext } from "./policyContext.js";
9
+ export class BasePolicy {
10
+ /**
11
+ * The Bouncer's active scope for the in-flight check (56.3). Defaults to
12
+ * `"global"` when the policy is used without a Bouncer context.
13
+ */
14
+ get scope() {
15
+ return getPolicyContext(this)?.scope ?? "global";
16
+ }
17
+ /**
18
+ * The resolved `EffectivePermissions` for the Bouncer's `(user, scope)` (56.3):
19
+ * role-derived ∪ ACL grants with global→tenant inheritance (per 56.1). A guest,
20
+ * a no-resolver Bouncer, or a standalone policy ⇒ empty permissions.
21
+ */
22
+ get permissions() {
23
+ return getPolicyContext(this)?.permissions ?? emptyPermissions("global");
24
+ }
25
+ /**
26
+ * Tenant-isolation helper (D5 — enforceable, not automatic). `true` under the
27
+ * `global` scope (single-tenant — no boundary); under `{ tenant: T }` it is
28
+ * `resource.tenantId === T`. A policy enforces explicitly, e.g.
29
+ * `if (!this.sameTenant(post)) return false`.
30
+ */
31
+ sameTenant(resource) {
32
+ const scope = this.scope;
33
+ return scope === "global" ? true : resource.tenantId === scope.tenant;
34
+ }
35
+ }
36
+ //# sourceMappingURL=BasePolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BasePolicy.js","sourceRoot":"","sources":["../../src/bouncer/BasePolicy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGxE,MAAM,OAAgB,UAAU;IAwB/B;;;OAGG;IACH,IAAc,KAAK;QAClB,OAAO,gBAAgB,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,QAAQ,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,IAAc,WAAW;QACxB,OAAO,gBAAgB,CAAC,IAAI,CAAC,EAAE,WAAW,IAAI,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC1E,CAAC;IAED;;;;;OAKG;IACO,UAAU,CAAC,QAAsC;QAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,KAAK,KAAK,CAAC,MAAM,CAAC;IACvE,CAAC;CACD"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Bouncer — Layer 2 (EVALUATION) of Warden's unified authorization (Epic 56).
3
+ *
4
+ * A Bouncer-faithful evaluation core (verified context7 `/adonisjs/bouncer`,
5
+ * 2026-06-01): standalone abilities via `Bouncer.ability`, class-based policies
6
+ * via `with(Policy)`, the four verbs `allows`/`denies`/`authorize`/`execute`,
7
+ * and guest-denied-by-default. The user is `UserPayload | null` (guest = null,
8
+ * D3); `authorize` throws `WARDEN_AUTHORIZATION_FAILURE` carrying `status: 403`
9
+ * (D2). 56.2 ships PURE mechanics — it does NOT consult the Layer-1
10
+ * `RightsResolver` (D4); abilities/policy methods receive `(user, ...args)` and
11
+ * the developer writes the predicate, exactly as Adonis does.
12
+ */
13
+ import type { UserPayload } from "../AuthManager.js";
14
+ import type { Scope } from "../rights/types.js";
15
+ import { AuthorizationResponse } from "./AuthorizationResponse.js";
16
+ import type { BasePolicy } from "./BasePolicy.js";
17
+ import { PolicyAuthorizer } from "./PolicyAuthorizer.js";
18
+ import type { Ability, AbilityOptions, AuthorizerResponse, BouncerContext } from "./types.js";
19
+ export declare class Bouncer {
20
+ #private;
21
+ constructor(user: UserPayload | null, abilities?: Record<string, Ability<never[]>>, policies?: Record<string, new () => BasePolicy>, context?: BouncerContext);
22
+ /** The active resolution scope (default `"global"`). */
23
+ get scope(): Scope;
24
+ /**
25
+ * Define a standalone ability. Guest (null user) denied by default; pass
26
+ * `{ allowGuest: true }` to let the callback run for a guest (D5 step 2).
27
+ */
28
+ static ability<Args extends unknown[]>(callback: (user: UserPayload, ...args: Args) => AuthorizerResponse): Ability<Args>;
29
+ static ability<Args extends unknown[]>(options: AbilityOptions, callback: (user: UserPayload | null, ...args: Args) => AuthorizerResponse): Ability<Args>;
30
+ /** Convenience — equivalent to `AuthorizationResponse.deny` (Adonis `bouncer.deny`). */
31
+ deny(message?: string, status?: number): AuthorizationResponse;
32
+ /** Open a policy for checks (D8 — fresh `new PolicyClass()` per check). */
33
+ with(policy: (new () => BasePolicy) | string): PolicyAuthorizer;
34
+ /** Run an ability check and resolve to the full response. */
35
+ execute<Args extends unknown[]>(ability: Ability<Args>, ...args: Args): Promise<AuthorizationResponse>;
36
+ execute(ability: string, ...args: unknown[]): Promise<AuthorizationResponse>;
37
+ /** True iff the ability is authorized. Never throws on denial. */
38
+ allows<Args extends unknown[]>(ability: Ability<Args>, ...args: Args): Promise<boolean>;
39
+ allows(ability: string, ...args: unknown[]): Promise<boolean>;
40
+ /** Boolean negation of {@link allows}. Never throws on denial. */
41
+ denies<Args extends unknown[]>(ability: Ability<Args>, ...args: Args): Promise<boolean>;
42
+ denies(ability: string, ...args: unknown[]): Promise<boolean>;
43
+ /** Resolves on allow; throws `WARDEN_AUTHORIZATION_FAILURE` on deny (D2). */
44
+ authorize<Args extends unknown[]>(ability: Ability<Args>, ...args: Args): Promise<void>;
45
+ authorize(ability: string, ...args: unknown[]): Promise<void>;
46
+ }
47
+ //# sourceMappingURL=Bouncer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Bouncer.d.ts","sourceRoot":"","sources":["../../src/bouncer/Bouncer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,KAAK,EAAwB,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,KAAK,EACX,OAAO,EACP,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,MAAM,YAAY,CAAC;AAEpB,qBAAa,OAAO;;gBAUlB,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,EAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,UAAU,CAAC,EAC/C,OAAO,CAAC,EAAE,cAAc;IASzB,wDAAwD;IACxD,IAAI,KAAK,IAAI,KAAK,CAEjB;IAkBD;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,IAAI,SAAS,OAAO,EAAE,EACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,kBAAkB,GAChE,OAAO,CAAC,IAAI,CAAC;IAChB,MAAM,CAAC,OAAO,CAAC,IAAI,SAAS,OAAO,EAAE,EACpC,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,kBAAkB,GACvE,OAAO,CAAC,IAAI,CAAC;IAmChB,wFAAwF;IACxF,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,qBAAqB;IAI9D,2EAA2E;IAC3E,IAAI,CAAC,MAAM,EAAE,CAAC,UAAU,UAAU,CAAC,GAAG,MAAM,GAAG,gBAAgB;IAY/D,6DAA6D;IAC7D,OAAO,CAAC,IAAI,SAAS,OAAO,EAAE,EAC7B,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,EACtB,GAAG,IAAI,EAAE,IAAI,GACX,OAAO,CAAC,qBAAqB,CAAC;IACjC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAQ5E,kEAAkE;IAClE,MAAM,CAAC,IAAI,SAAS,OAAO,EAAE,EAC5B,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,EACtB,GAAG,IAAI,EAAE,IAAI,GACX,OAAO,CAAC,OAAO,CAAC;IACnB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ7D,kEAAkE;IAClE,MAAM,CAAC,IAAI,SAAS,OAAO,EAAE,EAC5B,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,EACtB,GAAG,IAAI,EAAE,IAAI,GACX,OAAO,CAAC,OAAO,CAAC;IACnB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ7D,6EAA6E;IAC7E,SAAS,CAAC,IAAI,SAAS,OAAO,EAAE,EAC/B,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,EACtB,GAAG,IAAI,EAAE,IAAI,GACX,OAAO,CAAC,IAAI,CAAC;IAChB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAwD7D"}