toiljs 0.0.119 → 0.0.121

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,3 +1,4 @@
1
+ import { AuthReads } from 'toiljs/server/auth/AuthReads';
1
2
  // AuthService: the server half of the post-quantum auth primitive, available
2
3
  // as a no-import global (registered via the toilscript `--lib` mechanism, the
3
4
  // same way `crypto` is a global). The client derives an ML-DSA-44 keypair from
@@ -11,11 +12,12 @@
11
12
 
12
13
  import { DataWriter, DataReader } from 'data';
13
14
  import { SessionEpochs } from 'toiljs/server/auth/SessionEpochs';
14
- import { SessionRevocations, SessionDigest, RevokedSession } from 'toiljs/server/auth/SessionRevocations';
15
+ import { AuthStateDb, SessionDigest, RevokedSession } from 'toiljs/server/auth/AuthStateDb';
15
16
  import { HmacImportParams, HmacParams, ALG_SHA_256, FMT_RAW, USAGE_SIGN, USAGE_VERIFY } from 'crypto';
16
17
 
17
18
  import {
18
19
  Server,
20
+ Request,
19
21
  SecureCookies,
20
22
  Cookie,
21
23
  SameSite,
@@ -265,7 +267,7 @@ export namespace AuthService {
265
267
  const SESSION_VERSION: u8 = 2;
266
268
 
267
269
  /** Default session lifetime if `mintSession` is called without a ttl. */
268
- export const DEFAULT_SESSION_TTL_SECS: u64 = 86400; // 24h
270
+ export const DEFAULT_SESSION_TTL_SECS: u64 = 604800; // 7 days
269
271
 
270
272
  /**
271
273
  * Override the session-signing secret programmatically. OPTIONAL: by default
@@ -274,6 +276,7 @@ export namespace AuthService {
274
276
  * the env value for the current request; keep it out of any client bundle.
275
277
  */
276
278
  export function setSecret(secret: Uint8Array): void {
279
+ invalidateSession();
277
280
  __sessionSecret = secret;
278
281
  }
279
282
 
@@ -286,13 +289,33 @@ export namespace AuthService {
286
289
  export function getSessionBytes(): Uint8Array | null {
287
290
  const req = Server.currentRequest;
288
291
  if (req == null) return null;
292
+ if (!req.__authChecked) {
293
+ // Publish only after verification completes. A host fault must never
294
+ // turn into a cached anonymous result if the application handles it.
295
+ req.__authBytes = verifySession(req);
296
+ req.__authChecked = true;
297
+ }
298
+ if (Time.nowSeconds() >= req.__authExpires) return null;
299
+ const bytes = req.__authBytes;
300
+ // Callers may decode/mutate their copy without changing later auth checks.
301
+ return bytes == null ? null : bytes.slice();
302
+ }
303
+
304
+ function invalidateSession(): void {
305
+ const req = Server.currentRequest;
306
+ if (req == null) return;
307
+ req.__authChecked = false;
308
+ req.__authBytes = null;
309
+ req.__authExpires = 0;
310
+ }
289
311
 
312
+ function verifySession(req: Request): Uint8Array | null {
290
313
  const sealed = SecureCookies.signed(__resolveSessionSecret()).open(
291
314
  req.cookies(),
292
315
  sessionCookieName(__reqIsSecure()),
293
316
  );
294
317
  if (sealed == null) return null;
295
- if (SessionRevocations.sessions.lookup(new SessionDigest(crypto.toHex(crypto.sha256Text(sealed)))) != null) return null;
318
+ if (AuthReads.owner(AuthStateDb.revokedSessions, new SessionDigest(crypto.toHex(crypto.sha256Text(sealed)))) != null) return null;
296
319
 
297
320
  const payload = base64UrlDecode(sealed);
298
321
  if (payload == null) return null;
@@ -310,6 +333,7 @@ export namespace AuthService {
310
333
  if (Time.nowSeconds() >= exp) return null; // expired
311
334
  if (!SessionEpochs.allowed(userBytes, generation)) return null;
312
335
 
336
+ req.__authExpires = exp;
313
337
  return userBytes;
314
338
  }
315
339
 
@@ -397,6 +421,7 @@ export namespace AuthService {
397
421
 
398
422
  /** Invalidate all browser sessions authorized under the prior session generation. */
399
423
  export function revokeUserSessions(userData: Uint8Array): void {
424
+ invalidateSession();
400
425
  SessionEpochs.revoke(userData);
401
426
  }
402
427
 
@@ -406,7 +431,8 @@ export namespace AuthService {
406
431
  const req = Server.currentRequest!;
407
432
  const sealed = SecureCookies.signed(__resolveSessionSecret()).open(req.cookies(), sessionCookieName(__reqIsSecure()));
408
433
  if (sealed == null) return;
409
- SessionRevocations.sessions.claim(new SessionDigest(crypto.toHex(crypto.sha256Text(sealed))), new RevokedSession());
434
+ invalidateSession();
435
+ AuthStateDb.revokedSessions.claim(new SessionDigest(crypto.toHex(crypto.sha256Text(sealed))), new RevokedSession());
410
436
  }
411
437
 
412
438
  /** A `Set-Cookie` that immediately clears the session (logout). */
@@ -33,6 +33,12 @@ export class Request {
33
33
  headers: Array<Header>;
34
34
  body: Uint8Array;
35
35
 
36
+ // Auth verification belongs to this request object, never a process/tenant cache.
37
+ // Internal AuthService state; cleared before local revocations or secret changes.
38
+ __authChecked: bool = false;
39
+ __authBytes: Uint8Array | null = null;
40
+ __authExpires: u64 = 0;
41
+
36
42
  // Lazily parsed `Cookie` header, cached for the life of the request.
37
43
  private _cookies: CookieMap | null = null;
38
44
 
@@ -1,13 +0,0 @@
1
- @data
2
- export class SessionDigest {
3
- digest: string = '';
4
- constructor(digest: string = '') { this.digest = digest; }
5
- }
6
- @data
7
- export class RevokedSession {
8
- revoked: bool = true;
9
- }
10
- @database
11
- export class SessionRevocations {
12
- @collection static sessions: Unique<SessionDigest, RevokedSession>;
13
- }