@astram/guard 0.9.0 → 0.10.1

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.
@@ -1,17 +1,28 @@
1
1
  "use strict";
2
2
  /**
3
- * `AttemptLimiter` — a guard-owned, Redis-backed sliding-window attempt counter for
4
- * anti-brute-force on the auth surface (spec §5). ONE class, used at TWO call sites
5
- * with independent thresholds: `/auth/login` (H1 password-login lockout, default
6
- * 10/15min, keyed `login:<identityHint>`) and the `/auth/mfa` recovery branch
7
- * (default 5/15min, keyed `recovery:<principalId>`).
3
+ * `AttemptLimiter` — a guard-owned, Redis-backed FIXED-WINDOW attempt counter for
4
+ * anti-brute-force on the auth surface (spec §5). The window starts at the first counted
5
+ * attempt (`PEXPIRE windowMs`) and is not extended by later ones; the counter resets when
6
+ * it expires or on a success. One class, one instance per DI token with its own threshold:
7
+ * `SHIELD_LOGIN_LIMITER` (`login:pid:<id>`, shared by `/auth/login` and change-password),
8
+ * the per-IP login limiter, `SHIELD_MFA_LIMITER` (`mfa:pid:<id>`, `/auth/mfa` TOTP and the
9
+ * code-gated account MFA routes), `SHIELD_RECOVERY_LIMITER` (`/auth/mfa` recovery codes),
10
+ * and the forgot/reset-password limiters.
8
11
  *
9
12
  * ── CONTRACT ─────────────────────────────────────────────────────────────────
13
+ * consume(key) → Phase 9 #10, the gate for credential-verifying routes (login,
14
+ * /auth/mfa, change-password, the code-gated account MFA routes):
15
+ * atomically COUNTS this attempt first and returns
16
+ * `{ allowed: count <= maxAttempts, count }`. Parallel guesses get
17
+ * distinct counts, so at most `maxAttempts` reach the verifier.
18
+ * FAILS CLOSED: a Redis error throws (caller → 503).
19
+ * refund(key) → give back one consumed attempt that reached no verdict
20
+ * (transient outage; a step that is not a failure). Best-effort.
10
21
  * check(key) → `true` if the caller is UNDER the threshold (allowed);
11
22
  * `false` if AT/over it (locked out for the window).
12
23
  * recordFailure(key) → increment the counter; on the FIRST failure in a window,
13
24
  * set the window TTL (`PEXPIRE windowMs`) so the counter
14
- * auto-expires (a sliding window that resets on inactivity).
25
+ * auto-expires (a fixed window from the first failure).
15
26
  * RETURNS the new count so a caller can detect the LOCKOUT
16
27
  * TRANSITION (count === maxAttempts is the exact edge where
17
28
  * the NEXT check() flips to `false`) and act ONCE on it — e.g.
@@ -22,7 +33,10 @@
22
33
  * reset(key) → clear the counter (a SUCCESS resets — a legitimate user is
23
34
  * never penalized for their earlier fat-fingers).
24
35
  *
25
- * ── FAIL-OPEN-WITH-LOG (spec §5, the deliberate posture) ──────────────────────
36
+ * ── FAIL-OPEN-WITH-LOG (spec §5) — the read-only `check()` path only ──────────
37
+ * (`consume()` fails CLOSED instead — Phase 9 #10; the routes that use it also need
38
+ * Redis to bind or resolve a session, so failing closed locks no one out who could
39
+ * otherwise have signed in.) `check()` remains for forgot/reset-password, where:
26
40
  * A limiter differs from the credential path: a Redis outage must NOT lock out
27
41
  * every legitimate login/recovery. So `check()` FAILS OPEN on a Redis error —
28
42
  * it returns `true` (allowed) and LOGS the blip. This never grants access on its
@@ -31,17 +45,47 @@
31
45
  * count we couldn't write just means the window is a touch more lenient — never a
32
46
  * thrown error into the request path).
33
47
  *
34
- * ── ANTI-ENUMERATION ── the limiter itself is silent; the caller maps a `false`
35
- * from `check()` to the SAME generic 401 as a wrong password / wrong code, so a
36
- * locked-out account is indistinguishable from a bad credential.
48
+ * ── WHAT A LOCK ANSWERS ── the limiter itself is silent; the caller decides. On the
49
+ * unauthenticated `/auth/*` routes a lock is the SAME generic 401 as a wrong password /
50
+ * wrong code, so a locked-out account is indistinguishable from a bad credential. On the
51
+ * authenticated `/account/*` routes (change-password, MFA device remove, recovery-code
52
+ * regenerate) the caller already holds a session, so a lock answers 429 (Phase 8 #1,
53
+ * Phase 9 #3).
37
54
  *
38
55
  * ADR-0023: guard owns the infra adapter (ioredis); the class is a thin, injectable
39
56
  * collaborator the controller consumes via a DI token (`SHIELD_LOGIN_LIMITER` /
40
57
  * `SHIELD_RECOVERY_LIMITER`), each provided with the live Redis + its config.
41
58
  */
42
59
  Object.defineProperty(exports, "__esModule", { value: true });
43
- exports.AttemptLimiter = void 0;
60
+ exports.AttemptLimiter = exports.AttemptLimiterUnavailableError = exports.ATTEMPT_REFUND_SCRIPT = exports.ATTEMPT_CONSUME_SCRIPT = void 0;
44
61
  const common_1 = require("@nestjs/common");
62
+ /**
63
+ * Phase 9 #10 — the atomic increment-first step: count the attempt and arm the window TTL
64
+ * in one script, so the counter can never be left without an expiry between two commands.
65
+ * The TTL is also re-armed when the key somehow has none (`PTTL == -1`), so a counter
66
+ * left persistent by an older non-atomic write heals instead of locking forever.
67
+ */
68
+ exports.ATTEMPT_CONSUME_SCRIPT = `local n = redis.call('INCR', KEYS[1])
69
+ if n == 1 or redis.call('PTTL', KEYS[1]) == -1 then
70
+ redis.call('PEXPIRE', KEYS[1], ARGV[1])
71
+ end
72
+ return n`;
73
+ /** Phase 9 #10 — give back one consumed attempt; deletes the key when it reaches zero. */
74
+ exports.ATTEMPT_REFUND_SCRIPT = `if redis.call('EXISTS', KEYS[1]) == 0 then return 0 end
75
+ local n = redis.call('DECR', KEYS[1])
76
+ if n <= 0 then redis.call('DEL', KEYS[1]) end
77
+ return n`;
78
+ /**
79
+ * Thrown by {@link AttemptLimiter.consume} when Redis cannot answer: the gate fails CLOSED
80
+ * and the caller answers a retryable 503. Carries no key or Redis detail.
81
+ */
82
+ class AttemptLimiterUnavailableError extends Error {
83
+ constructor() {
84
+ super('attempt limiter unavailable');
85
+ this.name = 'AttemptLimiterUnavailableError';
86
+ }
87
+ }
88
+ exports.AttemptLimiterUnavailableError = AttemptLimiterUnavailableError;
45
89
  class AttemptLimiter {
46
90
  redis;
47
91
  cfg;
@@ -82,6 +126,51 @@ class AttemptLimiter {
82
126
  return true;
83
127
  }
84
128
  }
129
+ /**
130
+ * INCREMENT-FIRST attempt gate (Phase 9 #10). Atomically counts THIS attempt (one Lua
131
+ * `INCR`, arming the window TTL on the first hit) and reports whether it is within the
132
+ * budget (`count <= maxAttempts`). Call it BEFORE verifying the credential: N parallel
133
+ * guesses each get a distinct count, so at most `maxAttempts` of them ever reach the
134
+ * verifier (the old check → verify → recordFailure shape let every in-flight guess pass
135
+ * the check). The attempt stays counted when the credential is wrong; on success the
136
+ * caller calls {@link reset}; when the attempt never reached a verdict (a transient
137
+ * outage, or a step that is not a failure) the caller calls {@link refund}.
138
+ *
139
+ * `count === maxAttempts` on a FAILED attempt is the lockout edge (audit-on-the-edge).
140
+ *
141
+ * FAILS CLOSED: a Redis error throws {@link AttemptLimiterUnavailableError} (the caller
142
+ * answers a retryable 503). Unlike {@link check}, an outage never lets an unthrottled
143
+ * guess through.
144
+ */
145
+ async consume(key) {
146
+ let n;
147
+ try {
148
+ n = Number(await this.redis.eval(exports.ATTEMPT_CONSUME_SCRIPT, 1, this.k(key), String(this.cfg.windowMs)));
149
+ }
150
+ catch (err) {
151
+ AttemptLimiter.logger.warn(`attempt-limiter consume failed closed (denying) — Redis unavailable: ${describeError(err)}`);
152
+ throw new AttemptLimiterUnavailableError();
153
+ }
154
+ if (!Number.isFinite(n) || n < 1) {
155
+ // An answer that is not a count cannot prove the attempt is within budget.
156
+ throw new AttemptLimiterUnavailableError();
157
+ }
158
+ return { allowed: n <= this.cfg.maxAttempts, count: n };
159
+ }
160
+ /**
161
+ * Give back one attempt taken by {@link consume} that never reached a verdict (a
162
+ * transient dependency failure, or a step that is neither failure nor success, e.g. a
163
+ * password step that now needs a second factor). Removes the key when it reaches zero,
164
+ * so a refunded first attempt leaves no counter behind. Best-effort, never throws.
165
+ */
166
+ async refund(key) {
167
+ try {
168
+ await this.redis.eval(exports.ATTEMPT_REFUND_SCRIPT, 1, this.k(key));
169
+ }
170
+ catch (err) {
171
+ AttemptLimiter.logger.warn(`attempt-limiter refund best-effort miss — Redis unavailable: ${describeError(err)}`);
172
+ }
173
+ }
85
174
  /**
86
175
  * Increment the failure counter; set the window TTL on the FIRST failure so the
87
176
  * counter auto-expires after `windowMs` of inactivity. Best-effort — a write we
@@ -216,9 +216,11 @@ export declare const SHIELD_MEMBERSHIP_CHECKER: unique symbol;
216
216
  * typed hint (the DB collation decides which spellings are one account), else
217
217
  * `login:<sha256(canonical hint)>` for an unknown hint — no PII in the key; see
218
218
  * `rate-limit/canonical-hint.ts`. Default 10 failures / 15-min window.
219
- * Consumed at the TOP of `POST /auth/login` (`check` → generic 401 when locked) and on
220
- * the outcome (`recordFailure` on a rejected wrong-password; `reset` on `authenticated`
221
- * OR `mfa_required` — a correct password that merely needs MFA is NOT a failure).
219
+ * Phase 9 #10: every attempt is counted at the TOP of `POST /auth/login` (`consume`, atomic
220
+ * increment-first → generic 401 when over budget, 503 when Redis cannot answer); a rejected
221
+ * wrong-password stays counted, `authenticated` → `reset`, `mfa_required` or a transient
222
+ * outage → `refund` (a correct password that merely needs MFA is NOT a failure, nor a
223
+ * success until `/auth/mfa` completes). Shared with `POST /account/change-password`.
222
224
  */
223
225
  export declare const SHIELD_LOGIN_LIMITER: unique symbol;
224
226
  /**
@@ -227,8 +229,8 @@ export declare const SHIELD_LOGIN_LIMITER: unique symbol;
227
229
  * `LOGIN_IP_MAX_FAILURES` / `LOGIN_IP_WINDOW_MS`, see `AppConfig.loginIpLimit`). Caps
228
230
  * password spraying across MANY distinct hints from one client. Counts wrong-password
229
231
  * FAILURES only and is NEVER reset by a success (an attacker's own valid login must not
230
- * wipe the budget). Checked before the account lookup and the authenticator. Fails open
231
- * on a Redis blip (the limiter's posture). ENFORCED ONLY when the app set Express
232
+ * wipe the budget; a non-failure gives its attempt back). Counted before the account lookup
233
+ * and the authenticator (Phase 9 #10); fails closed (503) on a Redis error. ENFORCED ONLY when the app set Express
232
234
  * `trust proxy` explicitly (0 = no proxy, else the hop count): otherwise every client
233
235
  * behind a proxy shares one `req.ip`, so the limiter is switched OFF at boot with a
234
236
  * warning (`TrustProxyBootCheck`) rather than becoming a global login DoS.
@@ -237,16 +239,17 @@ export declare const SHIELD_LOGIN_IP_LIMITER: unique symbol;
237
239
  /**
238
240
  * The recovery-code {@link AttemptLimiter} (spec §5) — keyed `recovery:<principalId>`,
239
241
  * default 5 failures / 15-min window (recovery codes are higher-value than a rotating
240
- * 30-s TOTP). Consumed in the `/auth/mfa` recovery branch: `check` BEFORE `consume`
241
- * (locked → generic 401 without consulting the store), `recordFailure` on a wrong
242
- * code, `reset` on success.
242
+ * 30-s TOTP). Consumed in the `/auth/mfa` recovery branch: the attempt is counted
243
+ * (`consume`, Phase 9 #10) BEFORE the store is consulted (over budget → generic 401;
244
+ * Redis error → 503), a wrong code stays counted, `reset` on success.
243
245
  */
244
246
  export declare const SHIELD_RECOVERY_LIMITER: unique symbol;
245
247
  /**
246
248
  * The TOTP-step {@link AttemptLimiter} (security hardening #9) — keyed per account,
247
249
  * `mfa:pid:<principalId>`, default 5 failures / 15-min window. Consumed in the `/auth/mfa`
248
- * TOTP branch: `check` BEFORE the factor is verified (locked → generic 401 without
249
- * consulting the step-up), `recordFailure` on a rejected factor (the threshold edge audits
250
+ * TOTP branch and the code-gated `/account/mfa/*` routes: the attempt is counted
251
+ * (`consume`, Phase 9 #10) BEFORE the factor is verified (over budget → generic 401 /
252
+ * 429; Redis error → 503), a rejected factor stays counted (the threshold edge audits
250
253
  * `auth.mfa.locked` once), `reset` on success.
251
254
  */
252
255
  export declare const SHIELD_MFA_LIMITER: unique symbol;
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAEvF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,eAA2B,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,YAAY,eAAyB,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAW3E;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,eAAgC,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,qBAAqB,eAAkC,CAAC;AAErE;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,eAA8B,CAAC;AAE7D;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,eAAmC,CAAC;AAEvE;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,6BAA6B,eAA0C,CAAC;AAErF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,eAA4B,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,eAA2C,CAAC;AAEvF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,eAA0C,CAAC;AAErF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,eAAsC,CAAC;AAU7E;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,eAA+B,CAAC;AAE/D;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,eAA+B,CAAC;AAE/D;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,eAAkC,CAAC;AAErE;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,eAA4C,CAAC;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,eAAwC,CAAC"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAEvF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,eAA2B,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,YAAY,eAAyB,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAW3E;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,eAAgC,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,qBAAqB,eAAkC,CAAC;AAErE;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,eAA8B,CAAC;AAE7D;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,eAAmC,CAAC;AAEvE;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,6BAA6B,eAA0C,CAAC;AAErF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,eAA4B,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,eAA2C,CAAC;AAEvF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,eAA0C,CAAC;AAErF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,eAAsC,CAAC;AAU7E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,eAA+B,CAAC;AAE/D;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,eAA+B,CAAC;AAE/D;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,eAAkC,CAAC;AAErE;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,eAA4C,CAAC;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,eAAwC,CAAC"}
@@ -235,9 +235,11 @@ exports.SHIELD_MEMBERSHIP_CHECKER = Symbol('SHIELD_MEMBERSHIP_CHECKER');
235
235
  * typed hint (the DB collation decides which spellings are one account), else
236
236
  * `login:<sha256(canonical hint)>` for an unknown hint — no PII in the key; see
237
237
  * `rate-limit/canonical-hint.ts`. Default 10 failures / 15-min window.
238
- * Consumed at the TOP of `POST /auth/login` (`check` → generic 401 when locked) and on
239
- * the outcome (`recordFailure` on a rejected wrong-password; `reset` on `authenticated`
240
- * OR `mfa_required` — a correct password that merely needs MFA is NOT a failure).
238
+ * Phase 9 #10: every attempt is counted at the TOP of `POST /auth/login` (`consume`, atomic
239
+ * increment-first → generic 401 when over budget, 503 when Redis cannot answer); a rejected
240
+ * wrong-password stays counted, `authenticated` → `reset`, `mfa_required` or a transient
241
+ * outage → `refund` (a correct password that merely needs MFA is NOT a failure, nor a
242
+ * success until `/auth/mfa` completes). Shared with `POST /account/change-password`.
241
243
  */
242
244
  exports.SHIELD_LOGIN_LIMITER = Symbol('SHIELD_LOGIN_LIMITER');
243
245
  /**
@@ -246,8 +248,8 @@ exports.SHIELD_LOGIN_LIMITER = Symbol('SHIELD_LOGIN_LIMITER');
246
248
  * `LOGIN_IP_MAX_FAILURES` / `LOGIN_IP_WINDOW_MS`, see `AppConfig.loginIpLimit`). Caps
247
249
  * password spraying across MANY distinct hints from one client. Counts wrong-password
248
250
  * FAILURES only and is NEVER reset by a success (an attacker's own valid login must not
249
- * wipe the budget). Checked before the account lookup and the authenticator. Fails open
250
- * on a Redis blip (the limiter's posture). ENFORCED ONLY when the app set Express
251
+ * wipe the budget; a non-failure gives its attempt back). Counted before the account lookup
252
+ * and the authenticator (Phase 9 #10); fails closed (503) on a Redis error. ENFORCED ONLY when the app set Express
251
253
  * `trust proxy` explicitly (0 = no proxy, else the hop count): otherwise every client
252
254
  * behind a proxy shares one `req.ip`, so the limiter is switched OFF at boot with a
253
255
  * warning (`TrustProxyBootCheck`) rather than becoming a global login DoS.
@@ -256,16 +258,17 @@ exports.SHIELD_LOGIN_IP_LIMITER = Symbol('SHIELD_LOGIN_IP_LIMITER');
256
258
  /**
257
259
  * The recovery-code {@link AttemptLimiter} (spec §5) — keyed `recovery:<principalId>`,
258
260
  * default 5 failures / 15-min window (recovery codes are higher-value than a rotating
259
- * 30-s TOTP). Consumed in the `/auth/mfa` recovery branch: `check` BEFORE `consume`
260
- * (locked → generic 401 without consulting the store), `recordFailure` on a wrong
261
- * code, `reset` on success.
261
+ * 30-s TOTP). Consumed in the `/auth/mfa` recovery branch: the attempt is counted
262
+ * (`consume`, Phase 9 #10) BEFORE the store is consulted (over budget → generic 401;
263
+ * Redis error → 503), a wrong code stays counted, `reset` on success.
262
264
  */
263
265
  exports.SHIELD_RECOVERY_LIMITER = Symbol('SHIELD_RECOVERY_LIMITER');
264
266
  /**
265
267
  * The TOTP-step {@link AttemptLimiter} (security hardening #9) — keyed per account,
266
268
  * `mfa:pid:<principalId>`, default 5 failures / 15-min window. Consumed in the `/auth/mfa`
267
- * TOTP branch: `check` BEFORE the factor is verified (locked → generic 401 without
268
- * consulting the step-up), `recordFailure` on a rejected factor (the threshold edge audits
269
+ * TOTP branch and the code-gated `/account/mfa/*` routes: the attempt is counted
270
+ * (`consume`, Phase 9 #10) BEFORE the factor is verified (over budget → generic 401 /
271
+ * 429; Redis error → 503), a rejected factor stays counted (the threshold edge audits
269
272
  * `auth.mfa.locked` once), `reset` on success.
270
273
  */
271
274
  exports.SHIELD_MFA_LIMITER = Symbol('SHIELD_MFA_LIMITER');
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  export { guardMigrationsDir } from './migrations-dir.js';
18
18
  export { buildIdentityModuleRegistry } from './identity/module-registry.bridge.js';
19
19
  export { MODULE_KEY, IDENTITY_ACTIONS, actionsFor } from './identity/identity-actions.js';
20
- export { ShieldModule, SHIELD_DECISION_AUTHORITY, type ShieldModuleOptions, type ShieldModuleAsyncOptions, type RecordAuthorizationInputs, } from './module/shield.module.js';
20
+ export { ShieldModule, SHIELD_DECISION_AUTHORITY, SHIELD_RECORD_AUTHORIZATION, type ShieldModuleOptions, type ShieldModuleAsyncOptions, type RecordAuthorizationInputs, } from './module/shield.module.js';
21
21
  export { Public, Classified, CLASSIFICATION_METADATA, DEFAULT_CLASSIFIED_ACTION, normalizeClassification, } from './enforcement/classification.decorator.js';
22
22
  export type { Classification } from './enforcement/classification.decorator.js';
23
23
  export { OperationInventoryScanner } from './completeness/operation-inventory-scanner.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -16,9 +16,9 @@
16
16
  * `@nestjs/core`, `reflect-metadata`, `rxjs` so the host owns one copy of each.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.accessBindingTtlMs = exports.ROTATE_GRACE_MS = exports.SHIELD_SESSION_LIFETIMES = exports.HotRevokingSessionRecordStore = exports.SHIELD_CLOCK = exports.SHIELD_REFRESH_HASHER = exports.SHIELD_TOKEN_GENERATOR = exports.systemClockProvider = exports.refreshHasherProvider = exports.cryptoTokenGeneratorProvider = exports.SystemClock = exports.Sha256RefreshTokenHasher = exports.CryptoTokenGenerator = exports.SHIELD_HOT_SESSION_STORE = exports.RedisHotSessionStore = exports.SHIELD_SESSION_RECORD_STORE = exports.MariaDbSessionRecordStore = exports.EncryptedFileSecretsProvider = exports.LocalKekCrypto = exports.SHIELD_PASSWORD_HASHER = exports.argon2idHasherProvider = exports.Argon2idPasswordHasher = exports.stubDecisionAuthority = exports.GlobalEnforcementGuard = exports.NO_SESSION_FACTS = exports.SHIELD_SESSION_FACTS_ALS = exports.MariaDbApiKeyStore = exports.isApiKey = exports.extractBearerToken = exports.principalFromClaims = exports.resolveRequestAuth = exports.resolveRequestPrincipal = exports.SessionTokenBinder = exports.SHIELD_SERVICE_AUTHENTICATOR = exports.SHIELD_SESSION_TOKEN_BINDER = exports.CatalogGatabilityError = exports.CatalogReconciler = exports.OperationInventoryScanner = exports.normalizeClassification = exports.DEFAULT_CLASSIFIED_ACTION = exports.CLASSIFICATION_METADATA = exports.Classified = exports.Public = exports.SHIELD_DECISION_AUTHORITY = exports.ShieldModule = exports.actionsFor = exports.IDENTITY_ACTIONS = exports.MODULE_KEY = exports.buildIdentityModuleRegistry = exports.guardMigrationsDir = void 0;
20
- exports.DEFAULT_LOGIN_IP_LIMIT = exports.loadLoginIpLimit = exports.DEFAULT_REDIS_URL = exports.DEFAULT_DB_CONFIG = exports.assertRefreshHmacKey = exports.loadRefreshHmacKey = exports.loadSessionLifetimes = exports.loadRedisConfig = exports.loadDbConfig = exports.SCHEMA_SVC_POOL_HARD_CAP = exports.DEFAULT_POOL_BUDGET = exports.evaluatePoolBudget = exports.loadPoolBudget = exports.loadAppConfig = exports.APP_CONFIG = exports.assertPoolBudget = exports.BootSequence = exports.buildListScopeFn = exports.MariaDbSessionRestrictionStore = exports.SHIELD_SESSION_RESTRICTIONS = exports.SHIELD_PERMISSION_VERSION_CACHE = exports.DEFAULT_PERMISSION_VERSION_TTL_MS = exports.PERMISSION_VERSION_KEY_PREFIX = exports.RedisPermissionVersionCache = exports.withRedisPermissionVersion = exports.buildRecordAuthorization = exports.SHIELD_OPERATION_AUTHORITY_PORT = exports.ManifestOperationAuthorityAdapter = exports.GrantStoreUnavailableError = exports.InvalidGrantError = exports.GrantNotPermittedError = exports.SHIELD_CROSS_TENANT_GRANT_WRITER = exports.CrossTenantGrantWriter = exports.SHIELD_CROSS_TENANT_GRANT_PORT = exports.MariaDbCrossTenantGrantAdapter = exports.makeShareDeleteCascade = exports.ShareStoreUnavailableError = exports.ShareNotPermittedError = exports.SHIELD_RECORD_SHARE_WRITER = exports.RecordShareWriter = exports.SHIELD_RECORD_SHARE_PORT = exports.MariaDbRecordShareAdapter = exports.UnknownScopeFieldError = exports.translateListScope = exports.SHIELD_LIST_SCOPE_PORT = exports.MariaDbListScopeAdapter = exports.SHIELD_POLICY_STORE = exports.MariaDbPolicyStore = exports.SHIELD_RECORD_RULE_PORT = exports.MariaDbRecordPermissionRuleAdapter = void 0;
21
- exports.ScopeUnknownParentError = exports.ScopeDepthError = exports.ScopeCycleError = exports.buildClosureRows = exports.AccountController = exports.generateRecoveryCodes = exports.buildOtpauthUri = exports.generateTotpSecret = exports.SHIELD_LOGIN_TOTP_VERIFIER = exports.SHIELD_MFA_STEP_UP = exports.SHIELD_MFA_LIMITER = exports.challengeMatches = exports.credentialFingerprint = exports.CredentialMfaStepUp = exports.RedisPendingMfaStore = exports.PendingMfaStore = exports.AuthController = exports.SHIELD_SCOPE_SUBTREE_RESOLVER = exports.SHIELD_SESSION_FACTS_PROJECTOR = exports.hmacSha1 = exports.resolveTotpVerifier = exports.makeDefaultTotpVerifier = exports.AuthModule = exports.MariaDbCredentialResolver = exports.SHIELD_TOTP_VERIFIER_OVERRIDE = exports.SHIELD_MFA_DEVICE_STORE = exports.SHIELD_AUDIT_SINK = exports.SHIELD_SESSION_SWEEPER = exports.SHIELD_PROFILE_WRITER = exports.SHIELD_RECOVERY_CODE_STORE = exports.SHIELD_EMAIL_SENDER = exports.SHIELD_RESET_TOKEN_STORE = exports.SHIELD_CREDENTIAL_WRITER = exports.SHIELD_SESSION_AUTHORITY = exports.SHIELD_AUTHENTICATOR = exports.SHIELD_PROFILE_RESOLVER = exports.SHIELD_CREDENTIAL_RESOLVER = exports.SHIELD_REDIS = exports.isDevOrTestNodeEnv = void 0;
19
+ exports.ROTATE_GRACE_MS = exports.SHIELD_SESSION_LIFETIMES = exports.HotRevokingSessionRecordStore = exports.SHIELD_CLOCK = exports.SHIELD_REFRESH_HASHER = exports.SHIELD_TOKEN_GENERATOR = exports.systemClockProvider = exports.refreshHasherProvider = exports.cryptoTokenGeneratorProvider = exports.SystemClock = exports.Sha256RefreshTokenHasher = exports.CryptoTokenGenerator = exports.SHIELD_HOT_SESSION_STORE = exports.RedisHotSessionStore = exports.SHIELD_SESSION_RECORD_STORE = exports.MariaDbSessionRecordStore = exports.EncryptedFileSecretsProvider = exports.LocalKekCrypto = exports.SHIELD_PASSWORD_HASHER = exports.argon2idHasherProvider = exports.Argon2idPasswordHasher = exports.stubDecisionAuthority = exports.GlobalEnforcementGuard = exports.NO_SESSION_FACTS = exports.SHIELD_SESSION_FACTS_ALS = exports.MariaDbApiKeyStore = exports.isApiKey = exports.extractBearerToken = exports.principalFromClaims = exports.resolveRequestAuth = exports.resolveRequestPrincipal = exports.SessionTokenBinder = exports.SHIELD_SERVICE_AUTHENTICATOR = exports.SHIELD_SESSION_TOKEN_BINDER = exports.CatalogGatabilityError = exports.CatalogReconciler = exports.OperationInventoryScanner = exports.normalizeClassification = exports.DEFAULT_CLASSIFIED_ACTION = exports.CLASSIFICATION_METADATA = exports.Classified = exports.Public = exports.SHIELD_RECORD_AUTHORIZATION = exports.SHIELD_DECISION_AUTHORITY = exports.ShieldModule = exports.actionsFor = exports.IDENTITY_ACTIONS = exports.MODULE_KEY = exports.buildIdentityModuleRegistry = exports.guardMigrationsDir = void 0;
20
+ exports.loadLoginIpLimit = exports.DEFAULT_REDIS_URL = exports.DEFAULT_DB_CONFIG = exports.assertRefreshHmacKey = exports.loadRefreshHmacKey = exports.loadSessionLifetimes = exports.loadRedisConfig = exports.loadDbConfig = exports.SCHEMA_SVC_POOL_HARD_CAP = exports.DEFAULT_POOL_BUDGET = exports.evaluatePoolBudget = exports.loadPoolBudget = exports.loadAppConfig = exports.APP_CONFIG = exports.assertPoolBudget = exports.BootSequence = exports.buildListScopeFn = exports.MariaDbSessionRestrictionStore = exports.SHIELD_SESSION_RESTRICTIONS = exports.SHIELD_PERMISSION_VERSION_CACHE = exports.DEFAULT_PERMISSION_VERSION_TTL_MS = exports.PERMISSION_VERSION_KEY_PREFIX = exports.RedisPermissionVersionCache = exports.withRedisPermissionVersion = exports.buildRecordAuthorization = exports.SHIELD_OPERATION_AUTHORITY_PORT = exports.ManifestOperationAuthorityAdapter = exports.GrantStoreUnavailableError = exports.InvalidGrantError = exports.GrantNotPermittedError = exports.SHIELD_CROSS_TENANT_GRANT_WRITER = exports.CrossTenantGrantWriter = exports.SHIELD_CROSS_TENANT_GRANT_PORT = exports.MariaDbCrossTenantGrantAdapter = exports.makeShareDeleteCascade = exports.ShareStoreUnavailableError = exports.ShareNotPermittedError = exports.SHIELD_RECORD_SHARE_WRITER = exports.RecordShareWriter = exports.SHIELD_RECORD_SHARE_PORT = exports.MariaDbRecordShareAdapter = exports.UnknownScopeFieldError = exports.translateListScope = exports.SHIELD_LIST_SCOPE_PORT = exports.MariaDbListScopeAdapter = exports.SHIELD_POLICY_STORE = exports.MariaDbPolicyStore = exports.SHIELD_RECORD_RULE_PORT = exports.MariaDbRecordPermissionRuleAdapter = exports.accessBindingTtlMs = void 0;
21
+ exports.ScopeUnknownParentError = exports.ScopeDepthError = exports.ScopeCycleError = exports.buildClosureRows = exports.AccountController = exports.generateRecoveryCodes = exports.buildOtpauthUri = exports.generateTotpSecret = exports.SHIELD_LOGIN_TOTP_VERIFIER = exports.SHIELD_MFA_STEP_UP = exports.SHIELD_MFA_LIMITER = exports.challengeMatches = exports.credentialFingerprint = exports.CredentialMfaStepUp = exports.RedisPendingMfaStore = exports.PendingMfaStore = exports.AuthController = exports.SHIELD_SCOPE_SUBTREE_RESOLVER = exports.SHIELD_SESSION_FACTS_PROJECTOR = exports.hmacSha1 = exports.resolveTotpVerifier = exports.makeDefaultTotpVerifier = exports.AuthModule = exports.MariaDbCredentialResolver = exports.SHIELD_TOTP_VERIFIER_OVERRIDE = exports.SHIELD_MFA_DEVICE_STORE = exports.SHIELD_AUDIT_SINK = exports.SHIELD_SESSION_SWEEPER = exports.SHIELD_PROFILE_WRITER = exports.SHIELD_RECOVERY_CODE_STORE = exports.SHIELD_EMAIL_SENDER = exports.SHIELD_RESET_TOKEN_STORE = exports.SHIELD_CREDENTIAL_WRITER = exports.SHIELD_SESSION_AUTHORITY = exports.SHIELD_AUTHENTICATOR = exports.SHIELD_PROFILE_RESOLVER = exports.SHIELD_CREDENTIAL_RESOLVER = exports.SHIELD_REDIS = exports.isDevOrTestNodeEnv = exports.DEFAULT_LOGIN_IP_LIMIT = void 0;
22
22
  // Framework authz migrations (shield_authz DDL — permission / role_permission /
23
23
  // authz_permission_version). Absolute path a consumer's `@astram/migrate` setup merges in.
24
24
  var migrations_dir_js_1 = require("./migrations-dir.js");
@@ -40,6 +40,9 @@ Object.defineProperty(exports, "actionsFor", { enumerable: true, get: function (
40
40
  var shield_module_js_1 = require("./module/shield.module.js");
41
41
  Object.defineProperty(exports, "ShieldModule", { enumerable: true, get: function () { return shield_module_js_1.ShieldModule; } });
42
42
  Object.defineProperty(exports, "SHIELD_DECISION_AUTHORITY", { enumerable: true, get: function () { return shield_module_js_1.SHIELD_DECISION_AUTHORITY; } });
43
+ // Phase 9 #7: the record-authorization inputs a feature module (IdentityFeatureModule)
44
+ // provides for ShieldModule to build the request-path authority from (one source).
45
+ Object.defineProperty(exports, "SHIELD_RECORD_AUTHORIZATION", { enumerable: true, get: function () { return shield_module_js_1.SHIELD_RECORD_AUTHORIZATION; } });
43
46
  // Route classification (the consumer authors these on its controllers).
44
47
  var classification_decorator_js_1 = require("./enforcement/classification.decorator.js");
45
48
  Object.defineProperty(exports, "Public", { enumerable: true, get: function () { return classification_decorator_js_1.Public; } });
@@ -17,4 +17,16 @@
17
17
  * now import the ONE symbol from core, so it stays a single DI identity across the app.
18
18
  */
19
19
  export { SHIELD_DECISION_AUTHORITY } from '@astram/core';
20
+ /**
21
+ * The record-authorization inputs (`RecordAuthorizationInputs`: pool + app ports + registry) that
22
+ * a FEATURE module provides app-wide for `ShieldModule` to build the request-path decision
23
+ * authority from (security hardening Phase 9 #7). `@astram/identity`'s `IdentityFeatureModule`
24
+ * provides it, built from the same `extraObjects` as its own authorities, so the request path and
25
+ * the identity module can never judge with different registries. Guard never imports identity:
26
+ * this token is the seam.
27
+ *
28
+ * Exactly one source per app: this token, `ShieldModule.forRoot({ recordAuthorization })` or
29
+ * `ShieldModule.forRoot({ decisionAuthority })`. Two of them fail the boot.
30
+ */
31
+ export declare const SHIELD_RECORD_AUTHORIZATION: unique symbol;
20
32
  //# sourceMappingURL=shield-tokens.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shield-tokens.d.ts","sourceRoot":"","sources":["../../src/module/shield-tokens.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;GAUG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"shield-tokens.d.ts","sourceRoot":"","sources":["../../src/module/shield-tokens.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;GAUG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,eAAwC,CAAC"}
@@ -7,7 +7,7 @@
7
7
  * with no cycle. `shield.module.ts` re-exports these for the public barrel.
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.SHIELD_DECISION_AUTHORITY = void 0;
10
+ exports.SHIELD_RECORD_AUTHORIZATION = exports.SHIELD_DECISION_AUTHORITY = void 0;
11
11
  /**
12
12
  * The Core decision authority (`@astram/core`'s `DecisionAuthority`) — the single
13
13
  * ADR-0001 decision entry both the global enforcement guard and the engine's
@@ -21,3 +21,15 @@ exports.SHIELD_DECISION_AUTHORITY = void 0;
21
21
  */
22
22
  var core_1 = require("@astram/core");
23
23
  Object.defineProperty(exports, "SHIELD_DECISION_AUTHORITY", { enumerable: true, get: function () { return core_1.SHIELD_DECISION_AUTHORITY; } });
24
+ /**
25
+ * The record-authorization inputs (`RecordAuthorizationInputs`: pool + app ports + registry) that
26
+ * a FEATURE module provides app-wide for `ShieldModule` to build the request-path decision
27
+ * authority from (security hardening Phase 9 #7). `@astram/identity`'s `IdentityFeatureModule`
28
+ * provides it, built from the same `extraObjects` as its own authorities, so the request path and
29
+ * the identity module can never judge with different registries. Guard never imports identity:
30
+ * this token is the seam.
31
+ *
32
+ * Exactly one source per app: this token, `ShieldModule.forRoot({ recordAuthorization })` or
33
+ * `ShieldModule.forRoot({ decisionAuthority })`. Two of them fail the boot.
34
+ */
35
+ exports.SHIELD_RECORD_AUTHORIZATION = Symbol('SHIELD_RECORD_AUTHORIZATION');
@@ -15,8 +15,8 @@ import type { ScopeSubtreeResolver } from '../auth/ports/scope-subtree-resolver.
15
15
  import type { MembershipChecker } from '../auth/ports/membership-checker.js';
16
16
  import type { CoreDecisionAuthority } from '../enforcement/core-seam.js';
17
17
  import { type AppConfig } from '../config/app-config.js';
18
- import { SHIELD_DECISION_AUTHORITY } from './shield-tokens.js';
19
- export { SHIELD_DECISION_AUTHORITY };
18
+ import { SHIELD_DECISION_AUTHORITY, SHIELD_RECORD_AUTHORIZATION } from './shield-tokens.js';
19
+ export { SHIELD_DECISION_AUTHORITY, SHIELD_RECORD_AUTHORIZATION };
20
20
  export type { RecordAuthorizationInputs } from '../authz/wire-record-authorization.js';
21
21
  /**
22
22
  * Options for {@link ShieldModule.forRoot}. Intentionally minimal (ADR-0023 §7 —
@@ -49,6 +49,10 @@ export interface ShieldModuleOptions {
49
49
  *
50
50
  * MUTUALLY EXCLUSIVE with {@link decisionAuthority} — supplying BOTH is a misconfig and
51
51
  * throws at registration. Supplying NEITHER binds the fail-closed stub (unchanged).
52
+ *
53
+ * An app that imports `IdentityFeatureModule` (`@astram/identity` ≥ 0.7) passes NEITHER: the
54
+ * identity module provides the inputs under {@link SHIELD_RECORD_AUTHORIZATION} (Phase 9 #7),
55
+ * and this option next to it fails the boot (one source of `extraObjects`).
52
56
  */
53
57
  readonly recordAuthorization?: RecordAuthorizationInputs;
54
58
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"shield.module.d.ts","sourceRoot":"","sources":["../../src/module/shield.module.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shield.module.d.ts","sourceRoot":"","sources":["../../src/module/shield.module.ts"],"names":[],"mappings":"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"}
@@ -40,7 +40,7 @@ var __importStar = (this && this.__importStar) || (function () {
40
40
  })();
41
41
  var ShieldModule_1;
42
42
  Object.defineProperty(exports, "__esModule", { value: true });
43
- exports.ShieldModule = exports.SHIELD_DECISION_AUTHORITY = void 0;
43
+ exports.ShieldModule = exports.SHIELD_RECORD_AUTHORIZATION = exports.SHIELD_DECISION_AUTHORITY = void 0;
44
44
  const common_1 = require("@nestjs/common");
45
45
  const node_url_1 = require("node:url");
46
46
  const core_seam_js_1 = require("../enforcement/core-seam.js");
@@ -63,6 +63,7 @@ const tokens_js_1 = require("../auth/tokens.js");
63
63
  // EnforcementModule → guard). Re-exported here to keep the public import path stable.
64
64
  const shield_tokens_js_1 = require("./shield-tokens.js");
65
65
  Object.defineProperty(exports, "SHIELD_DECISION_AUTHORITY", { enumerable: true, get: function () { return shield_tokens_js_1.SHIELD_DECISION_AUTHORITY; } });
66
+ Object.defineProperty(exports, "SHIELD_RECORD_AUTHORIZATION", { enumerable: true, get: function () { return shield_tokens_js_1.SHIELD_RECORD_AUTHORIZATION; } });
66
67
  /**
67
68
  * A `Function`-wrapped dynamic `import()` that survives the CommonJS **build** — the same
68
69
  * shim as auth.module.ts's `evalImport`. Under `module: commonjs`, `tsc` would rewrite a
@@ -105,33 +106,45 @@ async function loadBuildRecordAuthorization() {
105
106
  }
106
107
  /**
107
108
  * Resolve the `SHIELD_DECISION_AUTHORITY` VALUE from the caller's options (the ONE seam,
108
- * ADR-0023 — only how its value is PRODUCED changes here). Precedence + mutual exclusion:
109
+ * ADR-0023 — only how its value is PRODUCED changes here). Exactly ONE source, else boot fails:
109
110
  *
110
- * - `recordAuthorization` PRESENT → the framework builds the REAL wired authority itself
111
- * (the four record-tier adapters constructed inside guard, un-overridable). This is the
112
- * Phase-5 path: the app can no longer supply or weaken those adapters.
111
+ * - {@link SHIELD_RECORD_AUTHORIZATION} PROVIDED (by a feature module — `IdentityFeatureModule`
112
+ * from `@astram/identity`, built from its own `extraObjects`) → the framework builds the REAL
113
+ * wired authority from those inputs (Phase 9 #7: one registry for the request path and the
114
+ * identity module's own authorities).
115
+ * - `recordAuthorization` PRESENT → the same, from inputs the app passes (an app without the
116
+ * identity module). The four record-tier adapters are constructed inside guard either way,
117
+ * un-overridable.
113
118
  * - `decisionAuthority` PRESENT (legacy passthrough) → bind the caller's pre-built
114
119
  * authority verbatim (kept for other consumers / back-compat).
115
- * - BOTH present → MISCONFIG: throw a clear error (they are mutually exclusive).
116
- * - NEITHER → the fail-closed {@link stubDecisionAuthority} (unchanged fallback).
120
+ * - MORE THAN ONE → MISCONFIG: throw a clear error (two sources could disagree).
121
+ * - NONE → the fail-closed {@link stubDecisionAuthority} (enforcement-only apps).
117
122
  *
118
- * Async because the `recordAuthorization` path lazy-loads the (core-touching) builder.
123
+ * Async because the record-authorization path lazy-loads the (core-touching) builder.
119
124
  */
120
125
  async function resolveDecisionAuthority(options, extras = {}) {
126
+ const provided = extras.providedRecordAuthorization ?? undefined;
127
+ if (provided && (options.recordAuthorization || options.decisionAuthority)) {
128
+ throw new Error('ShieldModule: a record authorization is already provided under SHIELD_RECORD_AUTHORIZATION ' +
129
+ '(IdentityFeatureModule.forRoot builds it from its own extraObjects), so ShieldModule.forRoot ' +
130
+ 'must not also receive `recordAuthorization` or `decisionAuthority`. Remove that option ' +
131
+ '(since @astram/identity 0.7 the identity module is the one source).');
132
+ }
121
133
  if (options.recordAuthorization && options.decisionAuthority) {
122
134
  throw new Error('ShieldModule: `recordAuthorization` and `decisionAuthority` are mutually exclusive — ' +
123
135
  'supply the record-authorization inputs (pool + app ports + registry) so the framework ' +
124
136
  'builds the decision authority, OR a pre-built `decisionAuthority`, but not both.');
125
137
  }
126
- if (options.recordAuthorization) {
138
+ const recordAuthorization = provided ?? options.recordAuthorization;
139
+ if (recordAuthorization) {
127
140
  const api = await loadBuildRecordAuthorization();
128
141
  const versionCache = extras.versionCache ?? undefined;
129
142
  // ADR-0035 default projector: cache the SAME facts the authority decides on (version-cache
130
143
  // wrapped, so a login projection also warms the Redis version).
131
144
  extras.defaultProjector?.attach(versionCache !== undefined
132
- ? api.withRedisPermissionVersion(options.recordAuthorization.permissionFacts, versionCache)
133
- : options.recordAuthorization.permissionFacts);
134
- return api.buildRecordAuthorization(options.recordAuthorization, versionCache !== undefined ? { versionCache } : {});
145
+ ? api.withRedisPermissionVersion(recordAuthorization.permissionFacts, versionCache)
146
+ : recordAuthorization.permissionFacts);
147
+ return api.buildRecordAuthorization(recordAuthorization, versionCache !== undefined ? { versionCache } : {});
135
148
  }
136
149
  return options.decisionAuthority ?? core_seam_js_1.stubDecisionAuthority;
137
150
  }
@@ -144,6 +157,14 @@ function defaultProjectorFor(auth, explicit) {
144
157
  }
145
158
  /** Inject the Redis version cache when AuthModule provides it (`auth: true`); absent otherwise. */
146
159
  const OPTIONAL_VERSION_CACHE = { token: tokens_js_1.SHIELD_PERMISSION_VERSION_CACHE, optional: true };
160
+ /**
161
+ * Phase 9 #7: the record-authorization inputs a feature module provides app-wide (global), e.g.
162
+ * `IdentityFeatureModule`. Optional: an enforcement-only app provides none.
163
+ */
164
+ const OPTIONAL_RECORD_AUTHORIZATION = {
165
+ token: shield_tokens_js_1.SHIELD_RECORD_AUTHORIZATION,
166
+ optional: true,
167
+ };
147
168
  /**
148
169
  * `ShieldModule` — the SINGLE registration seam (ADR-0023 §4).
149
170
  *
@@ -238,16 +259,17 @@ let ShieldModule = ShieldModule_1 = class ShieldModule {
238
259
  // stub paths resolve synchronously but flow through the same one seam. Both
239
260
  // supplied → throws (mutual exclusion).
240
261
  provide: shield_tokens_js_1.SHIELD_DECISION_AUTHORITY,
241
- useFactory: (versionCache) => resolveDecisionAuthority({
262
+ useFactory: (versionCache, providedRecordAuthorization) => resolveDecisionAuthority({
242
263
  ...(options.recordAuthorization
243
264
  ? { recordAuthorization: options.recordAuthorization }
244
265
  : {}),
245
266
  ...(options.decisionAuthority
246
267
  ? { decisionAuthority: options.decisionAuthority }
247
268
  : {}),
248
- }, { versionCache, defaultProjector }),
269
+ }, { versionCache, defaultProjector, providedRecordAuthorization }),
249
270
  // ADR-0035: the Redis version cache AuthModule provides under `auth: true`.
250
- inject: [OPTIONAL_VERSION_CACHE],
271
+ // Phase 9 #7: the record authorization a feature module provides (IdentityFeatureModule).
272
+ inject: [OPTIONAL_VERSION_CACHE, OPTIONAL_RECORD_AUTHORIZATION],
251
273
  },
252
274
  ],
253
275
  // Re-export the composed AuthModule app-wide ONLY when auth is opted in — its
@@ -289,11 +311,11 @@ let ShieldModule = ShieldModule_1 = class ShieldModule {
289
311
  // in its useFactory from the injected registrations) has the framework build the
290
312
  // real wired authority; `decisionAuthority` is the legacy passthrough; both →
291
313
  // throws; neither → stub. Async because the record path lazy-loads the builder.
292
- useFactory: (opts, versionCache) => resolveDecisionAuthority({
314
+ useFactory: (opts, versionCache, providedRecordAuthorization) => resolveDecisionAuthority({
293
315
  ...(opts.recordAuthorization ? { recordAuthorization: opts.recordAuthorization } : {}),
294
316
  ...(opts.decisionAuthority ? { decisionAuthority: opts.decisionAuthority } : {}),
295
- }, { versionCache, defaultProjector }),
296
- inject: [optionsToken, OPTIONAL_VERSION_CACHE],
317
+ }, { versionCache, defaultProjector, providedRecordAuthorization }),
318
+ inject: [optionsToken, OPTIONAL_VERSION_CACHE, OPTIONAL_RECORD_AUTHORIZATION],
297
319
  };
298
320
  // Same one-instance opt-in AuthModule as forRoot (imported + re-exported).
299
321
  // `credentialResolver` is STATIC on the async options (like `auth`) — a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astram/guard",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "license": "MIT",
5
5
  "description": "Astram framework — the enforcement guard + real-world adapters (the 'hands'). One global deny-by-default gate every request passes through (ADR-0002), classification decorators, boot-pool assertion (ADR-0018), the forRoot() registration seam, and the DB/Redis/crypto adapters that connect the sealed core to a real backend. Depends on @astram/core (ADR-0003, ADR-0023).",
6
6
  "type": "commonjs",
@@ -34,9 +34,9 @@
34
34
  "dependencies": {
35
35
  "argon2": "^0.41.1",
36
36
  "ioredis": "^5.4.1",
37
- "mariadb": "^3.4.0",
37
+ "mariadb": "^3.5.4",
38
38
  "@astram/core": "1.4.0",
39
- "@astram/engine": "0.5.0"
39
+ "@astram/engine": "0.5.1"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@nestjs/common": "^11",