@venturekit/auth 0.0.0-dev.20260701100017 → 0.0.0-dev.20260704225856

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 (62) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +5 -1
  4. package/dist/index.js.map +1 -1
  5. package/{migrations → dist/migrations}/vk_auth_001_verification_codes.sql +7 -4
  6. package/dist/migrations/vk_auth_003_role_scopes.sql +43 -0
  7. package/dist/roles/index.d.ts +5 -1
  8. package/dist/roles/index.d.ts.map +1 -1
  9. package/dist/roles/index.js +4 -1
  10. package/dist/roles/index.js.map +1 -1
  11. package/dist/roles/role-scopes.d.ts +92 -0
  12. package/dist/roles/role-scopes.d.ts.map +1 -0
  13. package/dist/roles/role-scopes.js +122 -0
  14. package/dist/roles/role-scopes.js.map +1 -0
  15. package/dist/server/cookies.d.ts +98 -13
  16. package/dist/server/cookies.d.ts.map +1 -1
  17. package/dist/server/cookies.js +77 -19
  18. package/dist/server/cookies.js.map +1 -1
  19. package/dist/server/federated-routes.d.ts +29 -22
  20. package/dist/server/federated-routes.d.ts.map +1 -1
  21. package/dist/server/federated-routes.js +31 -4
  22. package/dist/server/federated-routes.js.map +1 -1
  23. package/dist/server/federated.d.ts.map +1 -1
  24. package/dist/server/federated.js +7 -11
  25. package/dist/server/federated.js.map +1 -1
  26. package/dist/server/forgot-password.js +0 -1
  27. package/dist/server/forgot-password.js.map +1 -1
  28. package/dist/server/handoff-routes.d.ts +130 -0
  29. package/dist/server/handoff-routes.d.ts.map +1 -0
  30. package/dist/server/handoff-routes.js +178 -0
  31. package/dist/server/handoff-routes.js.map +1 -0
  32. package/dist/server/handoff.d.ts +112 -0
  33. package/dist/server/handoff.d.ts.map +1 -0
  34. package/dist/server/handoff.js +102 -0
  35. package/dist/server/handoff.js.map +1 -0
  36. package/dist/server/index.d.ts +11 -4
  37. package/dist/server/index.d.ts.map +1 -1
  38. package/dist/server/index.js +9 -3
  39. package/dist/server/index.js.map +1 -1
  40. package/dist/server/middleware.d.ts +35 -0
  41. package/dist/server/middleware.d.ts.map +1 -1
  42. package/dist/server/middleware.js +50 -10
  43. package/dist/server/middleware.js.map +1 -1
  44. package/dist/server/passwordless.d.ts +68 -0
  45. package/dist/server/passwordless.d.ts.map +1 -0
  46. package/dist/server/passwordless.js +136 -0
  47. package/dist/server/passwordless.js.map +1 -0
  48. package/dist/server/revoke.d.ts +10 -0
  49. package/dist/server/revoke.d.ts.map +1 -1
  50. package/dist/server/revoke.js +19 -2
  51. package/dist/server/revoke.js.map +1 -1
  52. package/dist/server/store/postgres.d.ts +35 -0
  53. package/dist/server/store/postgres.d.ts.map +1 -0
  54. package/dist/server/store/postgres.js +88 -0
  55. package/dist/server/store/postgres.js.map +1 -0
  56. package/dist/server/token-utils.d.ts +12 -2
  57. package/dist/server/token-utils.d.ts.map +1 -1
  58. package/dist/server/token-utils.js +9 -4
  59. package/dist/server/token-utils.js.map +1 -1
  60. package/package.json +21 -8
  61. package/src/migrations/vk_auth_001_verification_codes.sql +55 -0
  62. package/src/migrations/vk_auth_003_role_scopes.sql +43 -0
package/dist/index.d.ts CHANGED
@@ -6,7 +6,8 @@
6
6
  export * from './types/index.js';
7
7
  export { createCognitoConfig, DEFAULT_COGNITO_CONFIG, buildUserPoolConfig, } from './cognito/index.js';
8
8
  export type { UserPoolOutputs, UserPoolInfraConfig } from './cognito/user-pool.js';
9
- export { hasScope, hasAnyScope, hasAllScopes, getScopesForRoles, validateRolesConfig, } from './roles/index.js';
9
+ export { hasScope, hasAnyScope, hasAllScopes, getScopesForRoles, validateRolesConfig, listRoleScopes, getRoleScopes, setRoleScopes, grantScopeToRole, revokeScopeFromRole, createRoleScopesResolver, } from './roles/index.js';
10
+ export type { Querier, RoleScopeRow, RoleScopesLookup, RoleScopesResolver, RoleScopesResolverOptions, } from './roles/index.js';
10
11
  export { decodeTokenUnsafe, decodeToken, // deprecated alias of decodeTokenUnsafe
11
12
  verifyAndDecode, extractUserFromToken, isTokenExpired, getTokenExpiry, } from './session/index.js';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAGnF,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,iBAAiB,EACjB,WAAW,EAAE,wCAAwC;AACrD,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAGnF,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EAInB,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,OAAO,EACP,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,iBAAiB,EACjB,WAAW,EAAE,wCAAwC;AACrD,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -8,7 +8,11 @@ export * from './types/index.js';
8
8
  // Cognito
9
9
  export { createCognitoConfig, DEFAULT_COGNITO_CONFIG, buildUserPoolConfig, } from './cognito/index.js';
10
10
  // Roles
11
- export { hasScope, hasAnyScope, hasAllScopes, getScopesForRoles, validateRolesConfig, } from './roles/index.js';
11
+ export { hasScope, hasAnyScope, hasAllScopes, getScopesForRoles, validateRolesConfig,
12
+ // DB-backed role → scopes mapping (vk_role_scopes: structure ships
13
+ // with this package's migrations, rows owned by the app; cached
14
+ // resolver for scope-granting middleware + CRUD for management UIs)
15
+ listRoleScopes, getRoleScopes, setRoleScopes, grantScopeToRole, revokeScopeFromRole, createRoleScopesResolver, } from './roles/index.js';
12
16
  // Session
13
17
  export { decodeTokenUnsafe, decodeToken, // deprecated alias of decodeTokenUnsafe
14
18
  verifyAndDecode, extractUserFromToken, isTokenExpired, getTokenExpiry, } from './session/index.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,UAAU;AACV,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAG5B,QAAQ;AACR,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,UAAU;AACV,OAAO,EACL,iBAAiB,EACjB,WAAW,EAAE,wCAAwC;AACrD,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,UAAU;AACV,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAG5B,QAAQ;AACR,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,mBAAmB;AACnB,mEAAmE;AACnE,gEAAgE;AAChE,oEAAoE;AACpE,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAS1B,UAAU;AACV,OAAO,EACL,iBAAiB,EACjB,WAAW,EAAE,wCAAwC;AACrD,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAC"}
@@ -1,7 +1,10 @@
1
1
  -- vk_auth_001_verification_codes.sql
2
2
  --
3
- -- Owned by `@venturekit/auth`. Backs `VerificationCodeStore` (used by
4
- -- the OTP gating on self-service sign-up + sign-in / passwordless).
3
+ -- Owned by `@venturekit/auth`. Creates the `vk_verification_codes`
4
+ -- table backing `VerificationCodeStore` (used by the OTP gating on
5
+ -- self-service sign-up + sign-in / passwordless). The table is
6
+ -- `vk_`-prefixed per VentureKit's "package-owned tables are prefixed"
7
+ -- convention; consumer-side stores must query `vk_verification_codes`.
5
8
  --
6
9
  -- Rows are pre-Cognito state — created BEFORE a user exists in either
7
10
  -- Cognito or any consumer-side `users` mirror. The `identifier` is the
@@ -31,7 +34,7 @@
31
34
  -- expectation is that those projects will then leave their old
32
35
  -- migration file in place but stop hand-maintaining the schema here.
33
36
 
34
- CREATE TABLE IF NOT EXISTS verification_codes (
37
+ CREATE TABLE IF NOT EXISTS vk_verification_codes (
35
38
  channel TEXT NOT NULL,
36
39
  identifier TEXT NOT NULL,
37
40
  code_hash TEXT NOT NULL,
@@ -49,4 +52,4 @@ CREATE TABLE IF NOT EXISTS verification_codes (
49
52
  );
50
53
 
51
54
  CREATE INDEX IF NOT EXISTS idx_verification_codes_expires_at
52
- ON verification_codes (expires_at);
55
+ ON vk_verification_codes (expires_at);
@@ -0,0 +1,43 @@
1
+ -- @venturekit/auth — role → scopes mapping.
2
+ --
3
+ -- The DB is the SOURCE OF TRUTH for which scopes each role grants.
4
+ -- VentureKit ships the STRUCTURE (this migration, applied automatically
5
+ -- with every other package migration); the consuming app ships DATA
6
+ -- migrations to seed its own taxonomy, e.g.:
7
+ --
8
+ -- INSERT INTO vk_role_scopes (role, scope) VALUES
9
+ -- ('member', 'member.verified'),
10
+ -- ('moderator', 'member.verified'),
11
+ -- ('moderator', 'moderation.reports.read')
12
+ -- ON CONFLICT DO NOTHING;
13
+ --
14
+ -- Lives in @venturekit/auth — NOT in a pro package — because a role →
15
+ -- scopes matrix is baseline authorization for ANY app: single-tenant
16
+ -- apps key it by their global user role; multi-tenant apps feed it to
17
+ -- `@venturekit-pro/tenancy`'s scopes middleware
18
+ -- (`createTenantUserScopesMiddleware({ scopesByRole: resolver.lookup })`).
19
+ --
20
+ -- At runtime the mapping is read through `createRoleScopesResolver()`
21
+ -- (cached, TTL-refreshed). Mutations go through `setRoleScopes` /
22
+ -- `grantScopeToRole` / `revokeScopeFromRole`, so apps can build
23
+ -- management UIs on top — changes propagate to warm instances within
24
+ -- the resolver's TTL.
25
+ --
26
+ -- Deliberately GLOBAL (no tenant column): the mapping is the app's
27
+ -- authorization POLICY, not tenant data. Per-tenant overrides, if ever
28
+ -- needed, belong in a separate overlay table so the hot path stays a
29
+ -- single cached full-table read.
30
+
31
+ CREATE TABLE IF NOT EXISTS vk_role_scopes (
32
+ role text NOT NULL,
33
+ scope text NOT NULL,
34
+ created_at timestamptz NOT NULL DEFAULT now(),
35
+ PRIMARY KEY (role, scope)
36
+ );
37
+
38
+ COMMENT ON TABLE vk_role_scopes IS
39
+ 'Role → scope grants. Structure owned by @venturekit/auth; rows owned by the consuming app (seed via app migrations, manage via the role-scopes helpers).';
40
+
41
+ -- Scope grants are read by role on every request that misses the
42
+ -- resolver cache; the PK already serves (role, scope) lookups and
43
+ -- role-prefix scans.
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * Roles
3
3
  *
4
- * Utilities for managing roles and scopes.
4
+ * Utilities for managing roles and scopes — the static config helpers
5
+ * below, plus the DB-backed `vk_role_scopes` mapping (CRUD + cached
6
+ * resolver) in ./role-scopes.ts.
5
7
  */
8
+ export { listRoleScopes, getRoleScopes, setRoleScopes, grantScopeToRole, revokeScopeFromRole, createRoleScopesResolver, } from './role-scopes.js';
9
+ export type { Querier, RoleScopeRow, RoleScopesLookup, RoleScopesResolver, RoleScopesResolverOptions, } from './role-scopes.js';
6
10
  import type { RolesConfig } from '../types/index.js';
7
11
  /**
8
12
  * Check if user roles grant a specific scope
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/roles/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;GAEG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,GAClB,OAAO,CAQT;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,EAAE,WAAW,GAClB,OAAO,CAET;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,EAAE,WAAW,GAClB,OAAO,CAET;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,EAAE,WAAW,GAClB,MAAM,EAAE,CAWV;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAejE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/roles/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,OAAO,EACP,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;GAEG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,GAClB,OAAO,CAQT;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,EAAE,WAAW,GAClB,OAAO,CAET;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,EAAE,WAAW,GAClB,OAAO,CAET;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,EAAE,WAAW,GAClB,MAAM,EAAE,CAWV;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAejE"}
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Roles
3
3
  *
4
- * Utilities for managing roles and scopes.
4
+ * Utilities for managing roles and scopes — the static config helpers
5
+ * below, plus the DB-backed `vk_role_scopes` mapping (CRUD + cached
6
+ * resolver) in ./role-scopes.ts.
5
7
  */
8
+ export { listRoleScopes, getRoleScopes, setRoleScopes, grantScopeToRole, revokeScopeFromRole, createRoleScopesResolver, } from './role-scopes.js';
6
9
  /**
7
10
  * Check if user roles grant a specific scope
8
11
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/roles/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,MAAM,UAAU,QAAQ,CACtB,SAAmB,EACnB,KAAa,EACb,MAAmB;IAEnB,6BAA6B;IAC7B,IAAI,MAAM,CAAC,cAAc,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxD,OAAO,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,SAAmB,EACnB,MAAgB,EAChB,MAAmB;IAEnB,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAmB,EACnB,MAAgB,EAChB,MAAmB;IAEnB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAmB,EACnB,MAAmB;IAEnB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IAEjC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QACjE,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAmB;IACrD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjE,4BAA4B;IAC5B,IAAI,MAAM,CAAC,WAAW,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7D,MAAM,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,WAAW,kBAAkB,CAAC,CAAC;IACrE,CAAC;IAED,gCAAgC;IAChC,IAAI,MAAM,CAAC,cAAc,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;QACnE,MAAM,CAAC,IAAI,CAAC,qBAAqB,MAAM,CAAC,cAAc,kBAAkB,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/roles/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAW1B;;GAEG;AACH,MAAM,UAAU,QAAQ,CACtB,SAAmB,EACnB,KAAa,EACb,MAAmB;IAEnB,6BAA6B;IAC7B,IAAI,MAAM,CAAC,cAAc,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxD,OAAO,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,SAAmB,EACnB,MAAgB,EAChB,MAAmB;IAEnB,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAmB,EACnB,MAAgB,EAChB,MAAmB;IAEnB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAmB,EACnB,MAAmB;IAEnB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IAEjC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QACjE,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAmB;IACrD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjE,4BAA4B;IAC5B,IAAI,MAAM,CAAC,WAAW,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7D,MAAM,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,WAAW,kBAAkB,CAAC,CAAC;IACrE,CAAC;IAED,gCAAgC;IAChC,IAAI,MAAM,CAAC,cAAc,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;QACnE,MAAM,CAAC,IAAI,CAAC,qBAAqB,MAAM,CAAC,cAAc,kBAAkB,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * DB-backed role → scopes mapping over `vk_role_scopes`.
3
+ *
4
+ * The table structure ships with this package
5
+ * (`migrations/vk_auth_003_role_scopes.sql`); the ROWS are the
6
+ * consuming app's authorization policy — seeded by an app data
7
+ * migration and mutable at runtime through the helpers here, so apps
8
+ * can put a management UI on top without redeploying to change what a
9
+ * role may do.
10
+ *
11
+ * Sits in `@venturekit/auth` (not a pro package) because a role →
12
+ * scopes matrix is baseline authorization for ANY app:
13
+ * - single-tenant apps resolve their global user role through
14
+ * {@link createRoleScopesResolver} and grant the result;
15
+ * - multi-tenant apps pass `resolver.lookup` straight to
16
+ * `@venturekit-pro/tenancy`'s
17
+ * `createTenantUserScopesMiddleware({ scopesByRole })`.
18
+ *
19
+ * Reading on the hot path goes through the resolver: one full-table
20
+ * read, cached per process with a TTL (default 30s).
21
+ *
22
+ * **Consistency model.** Mutations are visible to the mutating
23
+ * instance immediately when it calls `resolver.invalidate()`, and to
24
+ * every other warm instance within one TTL — scope-mapping changes
25
+ * are rare and tolerate seconds of convergence.
26
+ *
27
+ * All helpers take a caller-supplied {@link Querier} — no hard
28
+ * dependency on `@venturekit/data`.
29
+ */
30
+ /**
31
+ * Minimal query interface — same shape as `@venturekit/data`'s `query`
32
+ * and the Queriers used across the workspace. Declared locally so this
33
+ * package carries no data-layer dependency.
34
+ */
35
+ export type Querier = <T = Record<string, unknown>[]>(sql: string, params?: unknown[]) => Promise<T>;
36
+ /** One `vk_role_scopes` row. */
37
+ export interface RoleScopeRow {
38
+ role: string;
39
+ scope: string;
40
+ }
41
+ /**
42
+ * Lookup signature scope-granting middleware consumes. Returning `[]`
43
+ * (unknown role) grants nothing — fail-closed.
44
+ */
45
+ export type RoleScopesLookup = (role: string) => Promise<readonly string[]> | readonly string[];
46
+ /** The full mapping, `{ role: [scope, …] }`, scopes sorted. */
47
+ export declare function listRoleScopes(querier: Querier): Promise<Record<string, string[]>>;
48
+ /** Scopes granted by one role (empty array when the role is unknown). */
49
+ export declare function getRoleScopes(querier: Querier, role: string): Promise<string[]>;
50
+ /**
51
+ * Replace a role's scope set atomically (single statement — a request
52
+ * racing the change sees either the old or the new complete set,
53
+ * never a partial one). An empty `scopes` array removes the role.
54
+ */
55
+ export declare function setRoleScopes(querier: Querier, role: string, scopes: readonly string[]): Promise<void>;
56
+ /** Add one grant. Idempotent. */
57
+ export declare function grantScopeToRole(querier: Querier, role: string, scope: string): Promise<void>;
58
+ /** Remove one grant. Idempotent. */
59
+ export declare function revokeScopeFromRole(querier: Querier, role: string, scope: string): Promise<void>;
60
+ export interface RoleScopesResolverOptions {
61
+ /** How the resolver reaches the DB. */
62
+ querier: Querier;
63
+ /**
64
+ * Cache lifetime, ms. Default 30 000. Governs how fast a mapping
65
+ * change propagates to OTHER warm instances (the mutating instance
66
+ * calls `invalidate()` and sees it immediately).
67
+ */
68
+ ttlMs?: number;
69
+ }
70
+ export interface RoleScopesResolver {
71
+ /**
72
+ * `(role) => scopes`. Pass as `scopesByRole` to tenancy's
73
+ * `createTenantUserScopesMiddleware`, or call directly from custom
74
+ * scope-granting middleware.
75
+ */
76
+ lookup: RoleScopesLookup;
77
+ /** Drop the cache — the next lookup reloads from the DB. */
78
+ invalidate(): void;
79
+ }
80
+ /**
81
+ * Build the cached mapping resolver.
82
+ *
83
+ * Failure semantics:
84
+ * - load fails and a previous snapshot exists → serve the stale
85
+ * snapshot (authorization availability beats freshness; the next
86
+ * lookup retries the load);
87
+ * - load fails with NO snapshot (cold start during a DB outage) →
88
+ * the error propagates, failing the request loudly rather than
89
+ * silently granting nothing to everyone.
90
+ */
91
+ export declare function createRoleScopesResolver(options: RoleScopesResolverOptions): RoleScopesResolver;
92
+ //# sourceMappingURL=role-scopes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-scopes.d.ts","sourceRoot":"","sources":["../../src/roles/role-scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAClD,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,OAAO,EAAE,KACf,OAAO,CAAC,CAAC,CAAC,CAAC;AAEhB,gCAAgC;AAChC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,GAAG,SAAS,MAAM,EAAE,CAAC;AAIpD,+DAA+D;AAC/D,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CASnC;AAED,yEAAyE;AACzE,wBAAsB,aAAa,CACjC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,EAAE,CAAC,CAMnB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,MAAM,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,iCAAiC;AACjC,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,oCAAoC;AACpC,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAKf;AAID,MAAM,WAAW,yBAAyB;IACxC,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,EAAE,gBAAgB,CAAC;IACzB,4DAA4D;IAC5D,UAAU,IAAI,IAAI,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,yBAAyB,GACjC,kBAAkB,CAyCpB"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * DB-backed role → scopes mapping over `vk_role_scopes`.
3
+ *
4
+ * The table structure ships with this package
5
+ * (`migrations/vk_auth_003_role_scopes.sql`); the ROWS are the
6
+ * consuming app's authorization policy — seeded by an app data
7
+ * migration and mutable at runtime through the helpers here, so apps
8
+ * can put a management UI on top without redeploying to change what a
9
+ * role may do.
10
+ *
11
+ * Sits in `@venturekit/auth` (not a pro package) because a role →
12
+ * scopes matrix is baseline authorization for ANY app:
13
+ * - single-tenant apps resolve their global user role through
14
+ * {@link createRoleScopesResolver} and grant the result;
15
+ * - multi-tenant apps pass `resolver.lookup` straight to
16
+ * `@venturekit-pro/tenancy`'s
17
+ * `createTenantUserScopesMiddleware({ scopesByRole })`.
18
+ *
19
+ * Reading on the hot path goes through the resolver: one full-table
20
+ * read, cached per process with a TTL (default 30s).
21
+ *
22
+ * **Consistency model.** Mutations are visible to the mutating
23
+ * instance immediately when it calls `resolver.invalidate()`, and to
24
+ * every other warm instance within one TTL — scope-mapping changes
25
+ * are rare and tolerate seconds of convergence.
26
+ *
27
+ * All helpers take a caller-supplied {@link Querier} — no hard
28
+ * dependency on `@venturekit/data`.
29
+ */
30
+ /* ─── CRUD ──────────────────────────────────────────────────────── */
31
+ /** The full mapping, `{ role: [scope, …] }`, scopes sorted. */
32
+ export async function listRoleScopes(querier) {
33
+ const rows = await querier(`SELECT role, scope FROM vk_role_scopes ORDER BY role, scope`);
34
+ const out = {};
35
+ for (const { role, scope } of rows) {
36
+ (out[role] ??= []).push(scope);
37
+ }
38
+ return out;
39
+ }
40
+ /** Scopes granted by one role (empty array when the role is unknown). */
41
+ export async function getRoleScopes(querier, role) {
42
+ const rows = await querier(`SELECT scope FROM vk_role_scopes WHERE role = $1 ORDER BY scope`, [role]);
43
+ return rows.map((r) => r.scope);
44
+ }
45
+ /**
46
+ * Replace a role's scope set atomically (single statement — a request
47
+ * racing the change sees either the old or the new complete set,
48
+ * never a partial one). An empty `scopes` array removes the role.
49
+ */
50
+ export async function setRoleScopes(querier, role, scopes) {
51
+ await querier(`WITH removed AS (
52
+ DELETE FROM vk_role_scopes WHERE role = $1
53
+ )
54
+ INSERT INTO vk_role_scopes (role, scope)
55
+ SELECT $1, s FROM unnest($2::text[]) AS s
56
+ ON CONFLICT DO NOTHING`, [role, [...scopes]]);
57
+ }
58
+ /** Add one grant. Idempotent. */
59
+ export async function grantScopeToRole(querier, role, scope) {
60
+ await querier(`INSERT INTO vk_role_scopes (role, scope)
61
+ VALUES ($1, $2)
62
+ ON CONFLICT DO NOTHING`, [role, scope]);
63
+ }
64
+ /** Remove one grant. Idempotent. */
65
+ export async function revokeScopeFromRole(querier, role, scope) {
66
+ await querier(`DELETE FROM vk_role_scopes WHERE role = $1 AND scope = $2`, [
67
+ role,
68
+ scope,
69
+ ]);
70
+ }
71
+ /**
72
+ * Build the cached mapping resolver.
73
+ *
74
+ * Failure semantics:
75
+ * - load fails and a previous snapshot exists → serve the stale
76
+ * snapshot (authorization availability beats freshness; the next
77
+ * lookup retries the load);
78
+ * - load fails with NO snapshot (cold start during a DB outage) →
79
+ * the error propagates, failing the request loudly rather than
80
+ * silently granting nothing to everyone.
81
+ */
82
+ export function createRoleScopesResolver(options) {
83
+ const ttlMs = options.ttlMs ?? 30_000;
84
+ let snapshot = null;
85
+ let loadedAt = 0;
86
+ let inflight = null;
87
+ const load = () => {
88
+ inflight ??= listRoleScopes(options.querier)
89
+ .then((mapping) => {
90
+ snapshot = mapping;
91
+ loadedAt = Date.now();
92
+ return mapping;
93
+ })
94
+ .finally(() => {
95
+ inflight = null;
96
+ });
97
+ return inflight;
98
+ };
99
+ const lookup = async (role) => {
100
+ if (!snapshot || Date.now() - loadedAt >= ttlMs) {
101
+ try {
102
+ await load();
103
+ }
104
+ catch (err) {
105
+ if (!snapshot)
106
+ throw err;
107
+ // Stale-but-present beats down: keep serving the last good
108
+ // snapshot; a later lookup retries.
109
+ loadedAt = Date.now();
110
+ }
111
+ }
112
+ return snapshot?.[role] ?? [];
113
+ };
114
+ return {
115
+ lookup,
116
+ invalidate() {
117
+ snapshot = null;
118
+ loadedAt = 0;
119
+ },
120
+ };
121
+ }
122
+ //# sourceMappingURL=role-scopes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-scopes.js","sourceRoot":"","sources":["../../src/roles/role-scopes.ts"],"names":[],"mappings":"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"}
@@ -6,20 +6,25 @@
6
6
  * - `HttpOnly` — JS in the browser cannot read these. The browser
7
7
  * attaches them automatically when fetching with
8
8
  * `credentials: 'include'`.
9
- * - `Secure` — set when {@link CookieOptions.secure} is true (default:
10
- * `process.env.NODE_ENV === 'production'`). Omitted in dev so cookies
11
- * work over plain HTTP localhost.
9
+ * - `Secure` — set when {@link CookieOptions.secure} is true. Defaults to
10
+ * ON everywhere except explicit local dev (`VENTURE_LOCAL === 'true'`,
11
+ * set by `vk dev`), so production cookies are always `Secure` while
12
+ * localhost over plain HTTP keeps working.
12
13
  * - `SameSite=Lax` for the id/access cookies, `SameSite=Strict` for
13
14
  * the refresh cookie. The refresh cookie is only ever sent to the
14
15
  * paths under {@link CookieOptions.refreshPath} (default `/auth`)
15
16
  * so it never leaks to non-auth handlers.
16
17
  * - `Path=/` for the id/access tokens, `Path=/auth` for refresh.
17
18
  *
18
- * The cookies are **host-only** (no `Domain` attribute) so they're
19
- * scoped to the host the response came from. The browser sends them on
20
- * every request to that host, including cross-origin XHR from a sibling
21
- * subdomain when `credentials: 'include'` is used and both hosts share
22
- * the same registrable domain.
19
+ * The cookies are **host-only** (no `Domain` attribute) by default, so
20
+ * they're scoped to the host the response came from. The browser sends
21
+ * them on every request to that host, including cross-origin XHR from a
22
+ * sibling subdomain when `credentials: 'include'` is used and both hosts
23
+ * share the same registrable domain. Apps that need the session to
24
+ * survive host changes — `www.` ↔ apex on a white-label domain, or
25
+ * cross-subdomain navigation in dev — pass {@link CookieOptions.domain}
26
+ * (usually computed per request with {@link resolveCookieDomain}) and
27
+ * every cookie gains a `Domain=` attribute.
23
28
  */
24
29
  export declare const ID_TOKEN_COOKIE = "vk_id_token";
25
30
  export declare const ACCESS_TOKEN_COOKIE = "vk_access_token";
@@ -34,20 +39,53 @@ export interface SessionTokens {
34
39
  }
35
40
  export interface CookieOptions {
36
41
  /**
37
- * Emit the `Secure` flag. Defaults to
38
- * `process.env.NODE_ENV === 'production'`.
42
+ * Emit the `Secure` flag. Defaults to ON unless `VENTURE_LOCAL === 'true'`
43
+ * (local dev, set by `vk dev`).
39
44
  */
40
45
  secure?: boolean;
41
46
  /**
42
47
  * Path scope for the refresh cookie. Defaults to `/auth`. The refresh
43
48
  * token is never sent on requests outside this prefix.
49
+ *
50
+ * Set this to the auth prefix **as the browser sees it**: an SPA that
51
+ * reaches the API through a same-origin `/api` proxy must use
52
+ * `/api/auth`, otherwise the browser never attaches the refresh
53
+ * cookie to `/api/auth/refresh` and the session dies when the id
54
+ * token expires.
44
55
  */
45
56
  refreshPath?: string;
46
57
  /**
47
58
  * Lifetime of the refresh cookie in seconds. Defaults to 30 days.
48
59
  */
49
60
  refreshMaxAgeSeconds?: number;
61
+ /**
62
+ * Emit a `Domain=` attribute on every cookie. Omit (default) for
63
+ * host-only cookies.
64
+ *
65
+ * Needed whenever the session must survive a host change: pinning to
66
+ * the apex so `www.example.com` and `example.com` share the session,
67
+ * or cross-subdomain navigation on a multi-tenant platform. Compute
68
+ * it per request with {@link resolveCookieDomain}.
69
+ *
70
+ * Clears MUST use the same domain as the set: a browser only deletes
71
+ * a cookie when the clear's Domain + Path match the original, so pass
72
+ * the same options to {@link buildClearSessionCookies}.
73
+ */
74
+ domain?: string;
50
75
  }
76
+ /**
77
+ * Default value for the cookie `Secure` flag.
78
+ *
79
+ * Secure is ON everywhere EXCEPT explicit local development, detected via
80
+ * `VENTURE_LOCAL === 'true'` (set by `vk dev`). A deployed Lambda never sets
81
+ * `VENTURE_LOCAL`, so production cookies always get `Secure`.
82
+ *
83
+ * This deliberately does NOT key off `NODE_ENV`: the AWS Lambda runtime does
84
+ * not set `NODE_ENV=production`, and the infra layer historically did not
85
+ * inject it, so the old `NODE_ENV === 'production'` default silently shipped
86
+ * session cookies WITHOUT `Secure` in production.
87
+ */
88
+ export declare function defaultSecure(): boolean;
51
89
  /**
52
90
  * Build the `Set-Cookie` header values to attach to a sign-in / refresh
53
91
  * response. Returns an array — API Gateway v2 supports multi-valued
@@ -75,8 +113,8 @@ export declare function buildClearSessionCookies(options?: CookieOptions): strin
75
113
  export declare function oauthStateCookieName(provider: string): string;
76
114
  export interface OAuthStateCookieOptions {
77
115
  /**
78
- * Emit the `Secure` flag. Defaults to
79
- * `process.env.NODE_ENV === 'production'`.
116
+ * Emit the `Secure` flag. Defaults to ON unless `VENTURE_LOCAL === 'true'`
117
+ * (local dev, set by `vk dev`).
80
118
  */
81
119
  secure?: boolean;
82
120
  /**
@@ -91,6 +129,14 @@ export interface OAuthStateCookieOptions {
91
129
  * sub-second TTLs.
92
130
  */
93
131
  maxAgeSeconds?: number;
132
+ /**
133
+ * Emit a `Domain=` attribute. Omit (default) for a host-only cookie —
134
+ * the start and callback requests normally hit the same browser
135
+ * origin, so host-only is almost always right. Accepted so callers
136
+ * threading one {@link CookieOptions} bag through a whole auth flow
137
+ * don't need a special case.
138
+ */
139
+ domain?: string;
94
140
  }
95
141
  /**
96
142
  * Build the `Set-Cookie` header value pinning the OAuth CSRF `state`
@@ -122,7 +168,46 @@ export declare function buildOAuthStateCookie(provider: string, value: string, o
122
168
  * by {@link buildOAuthStateCookie}. Emit at the end of the callback
123
169
  * flow so a stale `state` can't be replayed.
124
170
  */
125
- export declare function clearOAuthStateCookie(provider: string, options?: Pick<OAuthStateCookieOptions, 'secure' | 'path'>): string;
171
+ export declare function clearOAuthStateCookie(provider: string, options?: Pick<OAuthStateCookieOptions, 'secure' | 'path' | 'domain'>): string;
172
+ export interface CookieDomainOptions {
173
+ /**
174
+ * The platform's own registrable domain (e.g. `example.com` for a
175
+ * platform serving tenants on `<slug>.example.com`). Hosts equal to
176
+ * it or ending in `.<platformApex>` are treated as platform hosts;
177
+ * anything else is a white-label custom domain.
178
+ */
179
+ platformApex: string;
180
+ /**
181
+ * What to do for platform hosts:
182
+ * - `false` (default) — return `undefined`, i.e. host-only cookies.
183
+ * Right for prod multi-tenant platforms where each tenant
184
+ * subdomain is its own session boundary.
185
+ * - `true` — return the platform apex so the session is shared
186
+ * across every platform subdomain. Typical for dev, where the
187
+ * login page and the tenant SPA live on different subdomains of
188
+ * the same wildcard /etc/hosts entry.
189
+ * Custom domains are always pinned to their own apex regardless.
190
+ */
191
+ pinPlatform?: boolean;
192
+ }
193
+ /**
194
+ * Compute the {@link CookieOptions.domain} value for a request host on
195
+ * a white-label multi-tenant platform.
196
+ *
197
+ * Decision table (`platformApex: 'example.com'`):
198
+ * - `app.example.com` → `undefined` (host-only) — or `example.com`
199
+ * with `pinPlatform: true`.
200
+ * - `www.acme.io` / `acme.io` (custom domain) → `acme.io`, so the
201
+ * `www.` and bare hosts share the session.
202
+ * - `''` → `undefined`.
203
+ *
204
+ * Ports are stripped (`app.example.com:3000` → platform host), and
205
+ * matching is case-insensitive. Pass the BROWSER-facing host — behind
206
+ * a same-origin SPA proxy that's the `origin`/`referer` host, not the
207
+ * Lambda's `host` header (see `getRequestHost` in
208
+ * `@venturekit/runtime`).
209
+ */
210
+ export declare function resolveCookieDomain(requestHost: string, options: CookieDomainOptions): string | undefined;
126
211
  /**
127
212
  * Read a single cookie out of a `Cookie:` header value. Returns `null`
128
213
  * when the cookie is absent. Permissive: duplicates take the last
@@ -1 +1 @@
1
- {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/server/cookies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAC7C,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AACrD,eAAO,MAAM,oBAAoB,qBAAqB,CAAC;AAIvD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AA0CD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,aAAa,GACtB,MAAM,EAAE,CAqBV;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,EAAE,CAY1E;AAUD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAWR;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,IAAI,CAAC,uBAAuB,EAAE,QAAQ,GAAG,MAAM,CAAC,GACzD,MAAM,CAQR;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAC1C,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAef"}
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/server/cookies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAC7C,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AACrD,eAAO,MAAM,oBAAoB,qBAAqB,CAAC;AAIvD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAwCD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,aAAa,GACtB,MAAM,EAAE,CA0BV;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,EAAE,CAY1E;AAUD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CAYR;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,IAAI,CAAC,uBAAuB,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC,GACpE,MAAM,CAYR;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,mBAAmB,GAC3B,MAAM,GAAG,SAAS,CAUpB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAC1C,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAef"}