@astram/guard 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/account.controller.d.ts +3 -1
- package/dist/auth/account.controller.d.ts.map +1 -1
- package/dist/auth/account.controller.js +12 -5
- package/dist/auth/auth.controller.d.ts +130 -21
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +537 -143
- package/dist/auth/auth.module.d.ts +15 -1
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +212 -30
- package/dist/auth/connections.d.ts +1 -1
- package/dist/auth/connections.d.ts.map +1 -1
- package/dist/auth/connections.js +14 -3
- package/dist/auth/default-session-facts-projector.d.ts +26 -0
- package/dist/auth/default-session-facts-projector.d.ts.map +1 -0
- package/dist/auth/default-session-facts-projector.js +21 -0
- package/dist/auth/mfa-step-up.d.ts +32 -0
- package/dist/auth/mfa-step-up.d.ts.map +1 -0
- package/dist/auth/mfa-step-up.js +109 -0
- package/dist/auth/pending-mfa-store.d.ts +50 -52
- package/dist/auth/pending-mfa-store.d.ts.map +1 -1
- package/dist/auth/pending-mfa-store.js +103 -61
- package/dist/auth/ports/session-facts-projector.d.ts +5 -0
- package/dist/auth/ports/session-facts-projector.d.ts.map +1 -1
- package/dist/auth/rate-limit/canonical-hint.d.ts +24 -0
- package/dist/auth/rate-limit/canonical-hint.d.ts.map +1 -0
- package/dist/auth/rate-limit/canonical-hint.js +63 -0
- package/dist/auth/rate-limit/trust-proxy-check.d.ts +46 -0
- package/dist/auth/rate-limit/trust-proxy-check.d.ts.map +1 -0
- package/dist/auth/rate-limit/trust-proxy-check.js +94 -0
- package/dist/auth/tokens.d.ts +70 -11
- package/dist/auth/tokens.d.ts.map +1 -1
- package/dist/auth/tokens.js +71 -12
- package/dist/authentication/log-email-sender.d.ts +9 -4
- package/dist/authentication/log-email-sender.d.ts.map +1 -1
- package/dist/authentication/mariadb-api-key-store.d.ts +4 -1
- package/dist/authentication/mariadb-api-key-store.d.ts.map +1 -1
- package/dist/authentication/mariadb-api-key-store.js +31 -7
- package/dist/authentication/mariadb-credential-resolver.d.ts +79 -65
- package/dist/authentication/mariadb-credential-resolver.d.ts.map +1 -1
- package/dist/authentication/mariadb-credential-resolver.js +116 -73
- package/dist/authentication/mariadb-credential-writer.d.ts.map +1 -1
- package/dist/authentication/mariadb-or-totp-verifier.d.ts +21 -3
- package/dist/authentication/mariadb-or-totp-verifier.d.ts.map +1 -1
- package/dist/authentication/mariadb-or-totp-verifier.js +37 -6
- package/dist/authentication/mariadb-profile-resolver.d.ts.map +1 -1
- package/dist/authentication/mariadb-profile-writer.d.ts.map +1 -1
- package/dist/authentication/mariadb-session-sweeper.d.ts +14 -1
- package/dist/authentication/mariadb-session-sweeper.d.ts.map +1 -1
- package/dist/authentication/mariadb-session-sweeper.js +29 -11
- package/dist/authentication/noop-email-sender.d.ts +12 -0
- package/dist/authentication/noop-email-sender.d.ts.map +1 -0
- package/dist/authentication/noop-email-sender.js +9 -0
- package/dist/authz/cross-tenant-grant-writer.d.ts.map +1 -1
- package/dist/authz/manifest-operation-authority-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-cross-tenant-grant-adapter.js +3 -1
- package/dist/authz/mariadb-policy-store.js +1 -1
- package/dist/authz/mariadb-record-rule-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-record-rule-adapter.js +1 -4
- package/dist/authz/permission-version-cache.d.ts +38 -0
- package/dist/authz/permission-version-cache.d.ts.map +1 -0
- package/dist/authz/permission-version-cache.js +81 -0
- package/dist/authz/wire-record-authorization.d.ts +25 -1
- package/dist/authz/wire-record-authorization.d.ts.map +1 -1
- package/dist/authz/wire-record-authorization.js +55 -2
- package/dist/completeness/catalog-reconciler.d.ts.map +1 -1
- package/dist/config/app-config.d.ts +36 -0
- package/dist/config/app-config.d.ts.map +1 -1
- package/dist/config/app-config.js +44 -4
- package/dist/encryption/local-kek-crypto.d.ts.map +1 -1
- package/dist/enforcement/enforcement.module.d.ts.map +1 -1
- package/dist/enforcement/enforcement.module.js +9 -0
- package/dist/enforcement/engine-error.filter.d.ts +57 -0
- package/dist/enforcement/engine-error.filter.d.ts.map +1 -0
- package/dist/enforcement/engine-error.filter.js +118 -0
- package/dist/enforcement/global-enforcement.guard.d.ts +29 -2
- package/dist/enforcement/global-enforcement.guard.d.ts.map +1 -1
- package/dist/enforcement/global-enforcement.guard.js +143 -5
- package/dist/identity/auth-facts.controller.d.ts.map +1 -1
- package/dist/identity/permission-facts.adapter.d.ts.map +1 -1
- package/dist/identity/permission-facts.adapter.js +2 -3
- package/dist/index.d.ts +12 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +65 -25
- package/dist/module/shield.module.d.ts +9 -2
- package/dist/module/shield.module.d.ts.map +1 -1
- package/dist/module/shield.module.js +47 -38
- package/dist/request-auth/session-facts-als.d.ts +16 -1
- package/dist/request-auth/session-facts-als.d.ts.map +1 -1
- package/dist/request-auth/session-facts-als.js +23 -0
- package/dist/request-auth/session-token-binder.d.ts +166 -10
- package/dist/request-auth/session-token-binder.d.ts.map +1 -1
- package/dist/request-auth/session-token-binder.js +408 -28
- package/dist/secrets/encrypted-file-secrets.d.ts.map +1 -1
- package/dist/secrets/encrypted-file-secrets.js +9 -3
- package/dist/session/hot-revoking-session-record-store.d.ts +46 -0
- package/dist/session/hot-revoking-session-record-store.d.ts.map +1 -0
- package/dist/session/hot-revoking-session-record-store.js +58 -0
- package/dist/session/index.d.ts +3 -1
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +8 -1
- package/dist/session/mariadb-session-record-store.d.ts +6 -0
- package/dist/session/mariadb-session-record-store.d.ts.map +1 -1
- package/dist/session/mariadb-session-record-store.js +16 -1
- package/dist/session/session-lifetimes.d.ts +30 -0
- package/dist/session/session-lifetimes.d.ts.map +1 -0
- package/dist/session/session-lifetimes.js +21 -0
- package/dist/session/session-restriction-store.d.ts +43 -0
- package/dist/session/session-restriction-store.d.ts.map +1 -0
- package/dist/session/session-restriction-store.js +35 -0
- package/package.json +3 -3
- package/dist/auth/account.controller.js.map +0 -1
- package/dist/auth/auth.controller.js.map +0 -1
- package/dist/auth/auth.module.js.map +0 -1
- package/dist/auth/connections.js.map +0 -1
- package/dist/auth/dto.js.map +0 -1
- package/dist/auth/pending-mfa-store.js.map +0 -1
- package/dist/auth/persistence.module.js.map +0 -1
- package/dist/auth/ports/audit-sink.js.map +0 -1
- package/dist/auth/ports/credential-writer.js.map +0 -1
- package/dist/auth/ports/email-sender.js.map +0 -1
- package/dist/auth/ports/membership-checker.js.map +0 -1
- package/dist/auth/ports/mfa-device-store.js.map +0 -1
- package/dist/auth/ports/profile-writer.js.map +0 -1
- package/dist/auth/ports/recovery-code-store.js.map +0 -1
- package/dist/auth/ports/reset-token-store.js.map +0 -1
- package/dist/auth/ports/scope-subtree-resolver.js.map +0 -1
- package/dist/auth/ports/session-facts-projector.js.map +0 -1
- package/dist/auth/ports/session-sweeper.js.map +0 -1
- package/dist/auth/profile-resolver.js.map +0 -1
- package/dist/auth/rate-limit/attempt-limiter.js.map +0 -1
- package/dist/auth/tokens.js.map +0 -1
- package/dist/auth/totp-enrollment.js.map +0 -1
- package/dist/authentication/argon2id-password-hasher.js.map +0 -1
- package/dist/authentication/log-email-sender.js.map +0 -1
- package/dist/authentication/mariadb-credential-resolver.js.map +0 -1
- package/dist/authentication/mariadb-credential-writer.js.map +0 -1
- package/dist/authentication/mariadb-mfa-device-store.js.map +0 -1
- package/dist/authentication/mariadb-or-totp-verifier.js.map +0 -1
- package/dist/authentication/mariadb-profile-resolver.js.map +0 -1
- package/dist/authentication/mariadb-profile-writer.js.map +0 -1
- package/dist/authentication/mariadb-recovery-code-store.js.map +0 -1
- package/dist/authentication/mariadb-reset-token-store.js.map +0 -1
- package/dist/authentication/mariadb-session-sweeper.js.map +0 -1
- package/dist/authz/cross-tenant-grant-writer.js.map +0 -1
- package/dist/authz/list-scope-sql.js.map +0 -1
- package/dist/authz/manifest-operation-authority-adapter.js.map +0 -1
- package/dist/authz/mariadb-cross-tenant-grant-adapter.js.map +0 -1
- package/dist/authz/mariadb-list-scope-adapter.js.map +0 -1
- package/dist/authz/mariadb-policy-store.js.map +0 -1
- package/dist/authz/mariadb-record-rule-adapter.js.map +0 -1
- package/dist/authz/mariadb-record-share-adapter.js.map +0 -1
- package/dist/authz/record-share-cascade.js.map +0 -1
- package/dist/authz/record-share-writer.js.map +0 -1
- package/dist/authz/wire-record-authorization.js.map +0 -1
- package/dist/boot/boot-sequence.js.map +0 -1
- package/dist/boot/boot.module.js.map +0 -1
- package/dist/completeness/catalog-reconciler.js.map +0 -1
- package/dist/completeness/operation-inventory-scanner.js.map +0 -1
- package/dist/completeness/ports/catalog-store.js.map +0 -1
- package/dist/config/app-config.js.map +0 -1
- package/dist/config/config.module.js.map +0 -1
- package/dist/enforcement/classification.decorator.js.map +0 -1
- package/dist/enforcement/core-seam.js.map +0 -1
- package/dist/enforcement/enforcement.module.js.map +0 -1
- package/dist/enforcement/global-enforcement.guard.js.map +0 -1
- package/dist/enforcement/reenroll-allowlist.js.map +0 -1
- package/dist/identity/auth-facts.controller.js.map +0 -1
- package/dist/identity/identity-actions.js.map +0 -1
- package/dist/identity/module-registry.bridge.js.map +0 -1
- package/dist/identity/permission-facts.adapter.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/migrations-dir.js.map +0 -1
- package/dist/module/shield-tokens.js.map +0 -1
- package/dist/module/shield.module.js.map +0 -1
- package/dist/request-auth/request-principal.js.map +0 -1
- package/dist/request-auth/session-facts-als.js.map +0 -1
- package/dist/request-auth/session-token-binder.js.map +0 -1
- package/dist/request-auth/tokens.js.map +0 -1
- package/dist/scope/closure-builder.js.map +0 -1
- package/dist/session/crypto-adapters.js.map +0 -1
- package/dist/session/index.js.map +0 -1
- package/dist/session/mariadb-session-record-store.js.map +0 -1
- package/dist/session/redis-hot-session-store.js.map +0 -1
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
* establish/rotate time and reads it per request. Zero Core changes.
|
|
12
12
|
*
|
|
13
13
|
* The binding: `shield:at:<accessToken>` → JSON {@link BoundPrincipalClaims}
|
|
14
|
-
* ({ id, tenantId, roleIds, kind }), TTL
|
|
14
|
+
* ({ id, tenantId, roleIds, kind }), TTL ≤ the access-token lifetime (security hardening
|
|
15
|
+
* #8). Alongside it: `shield:sessclaims:<sessionId>` (the liveness entry, TTL = the refresh
|
|
16
|
+
* idle window when configured) and `shield:sessat:<sessionId>` (the session's current
|
|
17
|
+
* access token, so a rotation can grace-expire the old one and a revoke can drop it). The token
|
|
15
18
|
* stays fully opaque (carries no data itself); the mapping is server-side, so the
|
|
16
19
|
* session is server-REVOCABLE (drop the key) and the read is FAIL-CLOSED (absent or
|
|
17
20
|
* Redis-unavailable → no principal → the guard denies).
|
|
@@ -79,26 +82,74 @@ export interface BoundPrincipalClaims {
|
|
|
79
82
|
* never silently widen or empty a principal's reach.
|
|
80
83
|
*/
|
|
81
84
|
readonly scopeSubtreeNodeIds?: readonly string[];
|
|
85
|
+
/**
|
|
86
|
+
* OPTIONAL: the permission version of `tenantId` read just BEFORE `scopeSubtreeNodeIds` was
|
|
87
|
+
* resolved (security hardening Phase 1C). When gate 5 reloads facts at a NEWER version (identity
|
|
88
|
+
* bumps it on a company create/move/delete), the enforcement guard re-derives the subtree once
|
|
89
|
+
* and advances this stamp. Read before the resolve, so a bump racing the resolve leaves the stamp
|
|
90
|
+
* too OLD (one extra re-derive), never too new (a missed one).
|
|
91
|
+
*
|
|
92
|
+
* Only meaningful beside a subtree: `resolve()` drops it when the subtree is absent or the value
|
|
93
|
+
* is not a non-negative integer. ABSENT with a subtree (a legacy binding, or a version read that
|
|
94
|
+
* failed) → the next gate-5 reload re-derives once and stamps.
|
|
95
|
+
*/
|
|
96
|
+
readonly scopeSubtreeVersion?: number;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The `cachedFacts` slot a binding stores (ADR-0035): the version, the keys and the optional time
|
|
100
|
+
* bound. The projection's `roleIds` are NOT kept here — they are the claims' own `roleIds`, written
|
|
101
|
+
* in the same payload, so there is one copy of the roles.
|
|
102
|
+
*/
|
|
103
|
+
export declare function factsSlot(grants: CoarseGrants): CoarseGrants;
|
|
104
|
+
/** The projection's live roles when it reported them (a string array), else `undefined`. */
|
|
105
|
+
export declare function liveRoleIds(grants: CoarseGrants | null | undefined): string[] | undefined;
|
|
106
|
+
/** Lifetimes the binder applies (security hardening #8). Both optional for back-compat. */
|
|
107
|
+
export interface SessionTokenBinderOptions {
|
|
108
|
+
/**
|
|
109
|
+
* TTL of the session-claims (liveness) entry and the current-token pointer. The auth
|
|
110
|
+
* module passes the refresh idle window (`refreshIdleMs`): the entry must outlive every
|
|
111
|
+
* access token of a live session, and no longer. Absent → the legacy `4 × access TTL`.
|
|
112
|
+
*/
|
|
113
|
+
readonly sessionClaimsTtlMs?: number;
|
|
114
|
+
/**
|
|
115
|
+
* How long the PREVIOUS access token stays bound after `rebindOnRotate` (plan D7,
|
|
116
|
+
* default 30 s). It is then expired (`PEXPIRE`), so a rotated-away token never keeps
|
|
117
|
+
* its full access TTL.
|
|
118
|
+
*/
|
|
119
|
+
readonly rotateGraceMs?: number;
|
|
82
120
|
}
|
|
83
121
|
export declare class SessionTokenBinder {
|
|
84
122
|
private readonly redis;
|
|
85
|
-
|
|
123
|
+
private readonly options;
|
|
124
|
+
constructor(redis: Redis, options?: SessionTokenBinderOptions);
|
|
125
|
+
/** TTL for the session-claims entry + current-token pointer. */
|
|
126
|
+
private sessionTtl;
|
|
86
127
|
/**
|
|
87
128
|
* Bind an access token to its principal claims for `ttlMs` (the access-token
|
|
88
129
|
* lifetime). Called right after `SessionAuthority.establish` in the auth
|
|
89
130
|
* controller (it holds both the freshly-minted access token and the Principal).
|
|
90
131
|
* ALSO writes a session-id → claims entry so a later `rebindOnRotate` can carry the
|
|
91
132
|
* full claims (roles/kind/root) forward across a token rotation, where the
|
|
92
|
-
* durable session row alone lacks them
|
|
133
|
+
* durable session row alone lacks them, and records the session's CURRENT access
|
|
134
|
+
* token so a rotation can grace-expire it and a revoke can drop it.
|
|
93
135
|
*/
|
|
94
136
|
bind(accessToken: string, claims: BoundPrincipalClaims, ttlMs: number): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Grace-expire the session's previous access token after a rotation (plan D7): it stays
|
|
139
|
+
* usable for at most `rotateGraceMs` (never longer than it already had), then expires.
|
|
140
|
+
* Best-effort — a failure leaves the old binding to its own (≤ access TTL) expiry.
|
|
141
|
+
*/
|
|
142
|
+
private graceExpire;
|
|
95
143
|
/**
|
|
96
144
|
* On refresh/rotate the controller has the NEW access token + the session, but not
|
|
97
145
|
* the full Principal (roles/kind/root live only in the binding, not the
|
|
98
146
|
* durable session row). Re-bind by reading the session-claims entry and writing the
|
|
99
147
|
* new access-token binding with the SAME claims. Returns true if it carried claims
|
|
100
|
-
* forward, false if the session-claims entry was gone
|
|
101
|
-
*
|
|
148
|
+
* forward, false if the session-claims entry was gone or unreadable — the caller MUST
|
|
149
|
+
* then refuse the refresh (the auth controller answers 401: an unbound token is never
|
|
150
|
+
* handed out). A Redis error on the writes propagates (the caller fails closed too).
|
|
151
|
+
* The new binding's TTL is `ttlMs` (≤ the access TTL); the session's previous access
|
|
152
|
+
* token is grace-expired (≤ 30 s by default, plan D7).
|
|
102
153
|
*
|
|
103
154
|
* `freshFacts` (ADR-0011 eager projection at REFRESH):
|
|
104
155
|
* - `undefined` (omitted, the default) → carry whatever `cachedFacts` the session
|
|
@@ -110,7 +161,70 @@ export declare class SessionTokenBinder {
|
|
|
110
161
|
* - `null` → STRIP `cachedFacts` (a projection that yielded nothing → the next
|
|
111
162
|
* request DB-reloads; fail-closed).
|
|
112
163
|
*/
|
|
113
|
-
rebindOnRotate(newAccessToken: string, sessionId: string, ttlMs: number, freshFacts?: CoarseGrants | null, freshSubtree?: readonly string[]
|
|
164
|
+
rebindOnRotate(newAccessToken: string, sessionId: string, ttlMs: number, freshFacts?: CoarseGrants | null, freshSubtree?: readonly string[],
|
|
165
|
+
/**
|
|
166
|
+
* The permission version read just before `freshSubtree` was resolved (Phase 1C). Stamped
|
|
167
|
+
* when the fresh subtree is applied; absent → the stamp is dropped (unknown → re-derive once
|
|
168
|
+
* at the next reload). Ignored when no fresh subtree is applied (the carried subtree keeps its
|
|
169
|
+
* own stamp).
|
|
170
|
+
*/
|
|
171
|
+
freshSubtreeVersion?: number,
|
|
172
|
+
/**
|
|
173
|
+
* The principal's LIVE role ids from the same projection as `freshFacts` (ADR-0035, GAPS #4).
|
|
174
|
+
* Given → they REPLACE the carried roles (roles and facts are written together). Absent → the
|
|
175
|
+
* carried roles stay (a projector that does not report roles, or no projection at all).
|
|
176
|
+
*/
|
|
177
|
+
freshRoleIds?: readonly string[],
|
|
178
|
+
/**
|
|
179
|
+
* The node the fresh facts/roles/subtree were projected for (review follow-up: a scope-switch
|
|
180
|
+
* moves the claims off the login tenant). Given and the claims are now on a DIFFERENT node (a
|
|
181
|
+
* switch raced the refresh) → nothing fresh is applied and the cached facts are stripped (the
|
|
182
|
+
* next request reloads for the right node). Absent → applied as before.
|
|
183
|
+
*/
|
|
184
|
+
freshForTenantId?: string): Promise<boolean>;
|
|
185
|
+
/**
|
|
186
|
+
* SILENT RECOVERY (ADR-0013 / ADR-0035, GAPS #5): write a session binding REBUILT from durable
|
|
187
|
+
* state after the session-claims entry was lost (Redis restart/flush). The caller (refresh) has
|
|
188
|
+
* already rotated the durable refresh token and assembled `claims` from the durable session
|
|
189
|
+
* record + live roles + the root allowlist + the subtree resolver.
|
|
190
|
+
*
|
|
191
|
+
* The claims entry is written with `NX`: if one appeared meanwhile, nothing is overwritten and
|
|
192
|
+
* the rebuild is refused (`false`). The caller MUST then re-read the durable session and call
|
|
193
|
+
* {@link unbindSession} if it is no longer active (a logout racing the rebuild). A Redis error
|
|
194
|
+
* propagates (the caller fails closed).
|
|
195
|
+
*/
|
|
196
|
+
bindRecovered(accessToken: string, claims: BoundPrincipalClaims, ttlMs: number): Promise<boolean>;
|
|
197
|
+
/**
|
|
198
|
+
* The node a live session currently acts under (its claims' `tenantId` — the scope-switch target
|
|
199
|
+
* when it switched), or `null` when the claims are gone, unreadable, or Redis errors. Refresh uses
|
|
200
|
+
* it to project for the ACTIVE node; `null` → the durable login tenant.
|
|
201
|
+
*/
|
|
202
|
+
sessionTenant(sessionId: string): Promise<string | null>;
|
|
203
|
+
/**
|
|
204
|
+
* Whether Redis answers right now (Phase 2 review F1). Refresh asks BEFORE the durable rotate
|
|
205
|
+
* spends the refresh token, so a Redis outage answers a retryable 503 instead of burning the
|
|
206
|
+
* token on a binding that cannot be written. A client without `ping` (a test double) counts as
|
|
207
|
+
* reachable.
|
|
208
|
+
*
|
|
209
|
+
* Bounded by `timeoutMs` (default 1 s): the framework's client queues commands while
|
|
210
|
+
* disconnected (`maxRetriesPerRequest: null`), so an unbounded PING would wait for Redis to come
|
|
211
|
+
* back and the refresh would then spend the token for a client that already gave up.
|
|
212
|
+
*/
|
|
213
|
+
isAvailable(timeoutMs?: number): Promise<boolean>;
|
|
214
|
+
/**
|
|
215
|
+
* LIVE GRANTS re-bind (ADR-0035, GAPS #4/#6). Gate 5 reloaded this session's coarse grants (a
|
|
216
|
+
* version bump, an expired time bound, or no cache yet); write them, and the live roles from the
|
|
217
|
+
* same projection, onto the access-token entry AND the session-claims entry, so the next requests
|
|
218
|
+
* are served from the cache and every role-keyed path sees the new roles. Nothing else changes.
|
|
219
|
+
*
|
|
220
|
+
* Each entry is rewritten only when it still belongs to `sessionId` + `tenantId` (a concurrent
|
|
221
|
+
* scope-switch moved it → left alone) and `facts` is fresher than what it holds (a slower request
|
|
222
|
+
* never overwrites newer facts). Each keeps its remaining TTL and is written with `XX` (a logout
|
|
223
|
+
* that deleted it wins). Best-effort: any Redis error or miss → that entry is skipped; the caller
|
|
224
|
+
* still uses the reloaded roles for the current request. Returns `true` when the access-token
|
|
225
|
+
* entry was rewritten.
|
|
226
|
+
*/
|
|
227
|
+
refreshGrants(accessToken: string, sessionId: string, tenantId: string, facts: CoarseGrants, roleIds?: readonly string[]): Promise<boolean>;
|
|
114
228
|
/**
|
|
115
229
|
* SCOPE-SWITCH re-bind (Task 5.2, ADR-0027 / ADR-0024). A principal is switching which
|
|
116
230
|
* active node it acts under; re-bind its CURRENT session (the same access token — no
|
|
@@ -130,7 +244,39 @@ export declare class SessionTokenBinder {
|
|
|
130
244
|
* Returns `true` on a successful re-bind, `false` when the binding is gone (logged out /
|
|
131
245
|
* expired) or Redis is unavailable (the caller then denies the switch — fail-closed).
|
|
132
246
|
*/
|
|
133
|
-
rebindActiveScope(accessToken: string, sessionId: string, targetNodeId: string, subtree: readonly string[]
|
|
247
|
+
rebindActiveScope(accessToken: string, sessionId: string, targetNodeId: string, subtree: readonly string[],
|
|
248
|
+
/**
|
|
249
|
+
* The TARGET node's permission version read just before `subtree` was resolved (Phase 1C).
|
|
250
|
+
* The old node's stamp is always dropped (versions are per tenant); this one is stamped when
|
|
251
|
+
* given, else the stamp stays absent (→ one re-derive at the next reload).
|
|
252
|
+
*/
|
|
253
|
+
subtreeVersion?: number,
|
|
254
|
+
/**
|
|
255
|
+
* The coarse grants projected for the TARGET node (ADR-0035). Given → cached on the re-scoped
|
|
256
|
+
* binding instead of stripped (the first request under the new scope is served from cache).
|
|
257
|
+
* Absent/null → stripped (the next request reloads).
|
|
258
|
+
*/
|
|
259
|
+
targetFacts?: CoarseGrants | null,
|
|
260
|
+
/**
|
|
261
|
+
* The principal's LIVE roles at the TARGET node (ADR-0035, GAPS #4), from the same projection
|
|
262
|
+
* as `targetFacts`. Given → they replace the old node's roles (roles are per node). Absent →
|
|
263
|
+
* the carried roles stay until the first request's reload replaces them.
|
|
264
|
+
*/
|
|
265
|
+
targetRoleIds?: readonly string[]): Promise<boolean>;
|
|
266
|
+
/**
|
|
267
|
+
* RE-DERIVED SUBTREE re-bind (security hardening Phase 1C). The enforcement guard saw gate 5
|
|
268
|
+
* reload the facts at a permission version NEWER than the bound subtree's stamp and resolved
|
|
269
|
+
* the subtree again; write it (and its stamp) onto the access-token entry AND the session-claims
|
|
270
|
+
* entry, so this token and any later rotation carry it. Nothing else in the claims changes.
|
|
271
|
+
*
|
|
272
|
+
* Each entry is rewritten only when it still belongs to `sessionId` + `tenantId` (a concurrent
|
|
273
|
+
* scope-switch moved it elsewhere → left alone) and its stamp is absent or OLDER than `version`
|
|
274
|
+
* (a concurrent refresh already wrote something at least as fresh → left alone). Each keeps its
|
|
275
|
+
* remaining TTL (a re-derive never extends a session). Best-effort: any Redis error or miss →
|
|
276
|
+
* `false`; the caller still uses the fresh subtree for the current request and a later reload
|
|
277
|
+
* retries. Returns `true` when the access-token entry was rewritten.
|
|
278
|
+
*/
|
|
279
|
+
rederiveScopeSubtree(accessToken: string, sessionId: string, tenantId: string, subtree: readonly string[], version: number): Promise<boolean>;
|
|
134
280
|
/**
|
|
135
281
|
* Resolve an access token to its bound claims, or `null` if the binding is
|
|
136
282
|
* absent/expired. Fail-closed: any Redis error → `null` (the caller denies), never
|
|
@@ -155,9 +301,19 @@ export declare class SessionTokenBinder {
|
|
|
155
301
|
/** Drop an access-token binding. Idempotent. */
|
|
156
302
|
unbind(accessToken: string): Promise<void>;
|
|
157
303
|
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
304
|
+
* The THROWING variant of {@link unbindSession} (Phase 3 review P1): the same keys, but a
|
|
305
|
+
* Redis error PROPAGATES. An administrative revoke (an account disable through the session
|
|
306
|
+
* sweeper) must report a failed unbind so the admin can retry, instead of leaving the
|
|
307
|
+
* session's live access tokens valid until their TTL. Idempotent (absent keys are fine).
|
|
308
|
+
*/
|
|
309
|
+
unbindSessionStrict(sessionId: string): Promise<void>;
|
|
310
|
+
/**
|
|
311
|
+
* Revoke a session in Redis (logout, password reset, refresh reuse-kill, admin revoke):
|
|
312
|
+
* drop the session-claims entry — which the {@link resolve} liveness gate requires, so
|
|
313
|
+
* EVERY access token of the session is denied on its next request — and also the
|
|
314
|
+
* session's current access-token binding and pointer. A grace-period token from an
|
|
315
|
+
* earlier rotation is covered by the liveness gate and expires within the grace.
|
|
316
|
+
* Best-effort per key; TTLs clean up regardless ({@link unbindSessionStrict} throws instead).
|
|
161
317
|
*/
|
|
162
318
|
unbindSession(sessionId: string): Promise<void>;
|
|
163
319
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-token-binder.d.ts","sourceRoot":"","sources":["../../src/request-auth/session-token-binder.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"session-token-binder.d.ts","sourceRoot":"","sources":["../../src/request-auth/session-token-binder.ts"],"names":[],"mappings":"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"}
|