@dudousxd/nestjs-media-dashboard 0.7.2 → 0.9.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.
@@ -13,6 +13,13 @@ interface ConsoleSessionUser {
13
13
  type SessionHook = (request: unknown) => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null;
14
14
  /** Host hook for Mode B — validates submitted credentials from the built-in login screen. */
15
15
  type LoginHook = (username: string, password: string) => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null;
16
+ /**
17
+ * Re-checks a LIVE session when the cookie is slid forward. Runs at most once per `ttl/2` per
18
+ * session, so a DB round-trip here is cheap. Return `false` to revoke (the cookie is cleared and
19
+ * the request denied). Distinct from `session`: that hook reads the host's auth off a fresh
20
+ * request, which a console XHR does not carry — this one receives the already-minted session.
21
+ */
22
+ type RevalidateHook = (session: ConsoleSessionUser) => Promise<boolean> | boolean;
16
23
  /** Author-facing `auth` option on `MediaDashboardModule.forRoot`. Mirrors telescope's dashboardAuth. */
17
24
  interface ConsoleAuthOptions {
18
25
  /** REQUIRED HMAC-SHA256 signing key. Missing/empty => boot error (fail closed). */
@@ -23,6 +30,8 @@ interface ConsoleAuthOptions {
23
30
  session?: SessionHook;
24
31
  /** Mode B: validate credentials from the built-in login screen. */
25
32
  login?: LoginHook;
33
+ /** Re-checks the session on sliding renewal. Not a mode — it cannot mint a session on its own. */
34
+ revalidate?: RevalidateHook;
26
35
  }
27
36
 
28
37
  interface MediaDashboardOptions {
@@ -46,7 +55,8 @@ interface MediaDashboardOptions {
46
55
  * Gate the console (SPA + API) behind a built-in session-cookie login, telescope-style. Omit to
47
56
  * leave the console open (front it with your own guard). When set, the SPA renders a login screen
48
57
  * until a valid cookie exists; supply a `login(username, password)` and/or `session(request)`
49
- * hook that returns a session user (or `null` to deny) — see {@link ConsoleAuthOptions}.
58
+ * hook that returns a session user (or `null` to deny), and optionally a `revalidate(session)`
59
+ * hook that re-checks the user when the cookie is slid forward — see {@link ConsoleAuthOptions}.
50
60
  */
51
61
  auth?: ConsoleAuthOptions;
52
62
  /**
@@ -42,6 +42,9 @@ function resolveConsoleAuth(options) {
42
42
  } : {},
43
43
  ...options.login ? {
44
44
  login: options.login
45
+ } : {},
46
+ ...options.revalidate ? {
47
+ revalidate: options.revalidate
45
48
  } : {}
46
49
  };
47
50
  }
@@ -316,14 +319,16 @@ var MediaConsoleGuard = class {
316
319
  this.auth = auth;
317
320
  this.cookiePath = cookiePath;
318
321
  }
319
- canActivate(context) {
322
+ async canActivate(context) {
320
323
  if (!this.auth) return true;
321
324
  const http = context.switchToHttp();
322
325
  const request = http.getRequest();
323
326
  const session = this.verifyRequestSession(request);
324
327
  if (!session) throw new UnauthorizedException();
325
328
  attachSession(request, session);
326
- this.maybeRenew(http.getResponse(), request, session);
329
+ if (!await this.maybeRenew(http.getResponse(), request, session)) {
330
+ throw new UnauthorizedException();
331
+ }
327
332
  return true;
328
333
  }
329
334
  verifyRequestSession(request) {
@@ -335,26 +340,46 @@ var MediaConsoleGuard = class {
335
340
  });
336
341
  }
337
342
  /**
338
- * Sliding renewal: when a valid cookie is past half its TTL, re-issue a fresh one so active users
339
- * never get logged out mid-session. Appends a new Set-Cookie (preserving any others already set).
343
+ * Sliding renewal + revalidation: when a valid cookie is past half its TTL, re-issue a fresh one
344
+ * so active users never get logged out mid-session — but first let the host's `revalidate` hook
345
+ * re-check the user, so a deactivated or demoted operator loses access instead of riding a
346
+ * self-renewing cookie. Returns `false` when the session was revoked (cookie already cleared).
340
347
  */
341
- maybeRenew(response, request, session) {
342
- if (!this.auth) return;
348
+ async maybeRenew(response, request, session) {
349
+ if (!this.auth) return true;
343
350
  const now = Date.now();
344
- if (now - session.iat <= this.auth.ttlMs / 2) return;
345
- issueSessionCookie({
351
+ if (now - session.iat <= this.auth.ttlMs / 2) return true;
352
+ const user = {
346
353
  id: session.sub,
347
354
  ...session.name !== void 0 ? {
348
355
  name: session.name
349
356
  } : {},
350
357
  roles: session.roles
351
- }, {
358
+ };
359
+ if (this.auth.revalidate) {
360
+ let allowed;
361
+ try {
362
+ allowed = await this.auth.revalidate(user);
363
+ } catch {
364
+ allowed = false;
365
+ }
366
+ if (!allowed) {
367
+ clearSessionCookie({
368
+ cookiePath: this.cookiePath ?? "/",
369
+ request,
370
+ response
371
+ });
372
+ return false;
373
+ }
374
+ }
375
+ issueSessionCookie(user, {
352
376
  auth: this.auth,
353
377
  cookiePath: this.cookiePath ?? "/",
354
378
  request,
355
379
  response,
356
380
  now
357
381
  });
382
+ return true;
358
383
  }
359
384
  };
360
385
  MediaConsoleGuard = _ts_decorate([