@venturekit/auth 0.0.1 → 0.0.2

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 (54) 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/dist/migrations/vk_auth_003_role_scopes.sql +43 -0
  6. package/dist/roles/index.d.ts +5 -1
  7. package/dist/roles/index.d.ts.map +1 -1
  8. package/dist/roles/index.js +4 -1
  9. package/dist/roles/index.js.map +1 -1
  10. package/dist/roles/role-scopes.d.ts +92 -0
  11. package/dist/roles/role-scopes.d.ts.map +1 -0
  12. package/dist/roles/role-scopes.js +122 -0
  13. package/dist/roles/role-scopes.js.map +1 -0
  14. package/dist/server/cookies.d.ts +77 -6
  15. package/dist/server/cookies.d.ts.map +1 -1
  16. package/dist/server/cookies.js +55 -13
  17. package/dist/server/cookies.js.map +1 -1
  18. package/dist/server/federated-routes.d.ts +29 -22
  19. package/dist/server/federated-routes.d.ts.map +1 -1
  20. package/dist/server/federated-routes.js +31 -4
  21. package/dist/server/federated-routes.js.map +1 -1
  22. package/dist/server/federated.d.ts.map +1 -1
  23. package/dist/server/federated.js +7 -11
  24. package/dist/server/federated.js.map +1 -1
  25. package/dist/server/handoff-routes.d.ts +130 -0
  26. package/dist/server/handoff-routes.d.ts.map +1 -0
  27. package/dist/server/handoff-routes.js +178 -0
  28. package/dist/server/handoff-routes.js.map +1 -0
  29. package/dist/server/handoff.d.ts +112 -0
  30. package/dist/server/handoff.d.ts.map +1 -0
  31. package/dist/server/handoff.js +102 -0
  32. package/dist/server/handoff.js.map +1 -0
  33. package/dist/server/index.d.ts +10 -3
  34. package/dist/server/index.d.ts.map +1 -1
  35. package/dist/server/index.js +8 -2
  36. package/dist/server/index.js.map +1 -1
  37. package/dist/server/passwordless.d.ts +68 -0
  38. package/dist/server/passwordless.d.ts.map +1 -0
  39. package/dist/server/passwordless.js +136 -0
  40. package/dist/server/passwordless.js.map +1 -0
  41. package/dist/server/revoke.d.ts +10 -0
  42. package/dist/server/revoke.d.ts.map +1 -1
  43. package/dist/server/revoke.js +19 -1
  44. package/dist/server/revoke.js.map +1 -1
  45. package/dist/server/store/postgres.d.ts +35 -0
  46. package/dist/server/store/postgres.d.ts.map +1 -0
  47. package/dist/server/store/postgres.js +88 -0
  48. package/dist/server/store/postgres.js.map +1 -0
  49. package/dist/server/token-utils.d.ts +12 -2
  50. package/dist/server/token-utils.d.ts.map +1 -1
  51. package/dist/server/token-utils.js +9 -4
  52. package/dist/server/token-utils.js.map +1 -1
  53. package/package.json +13 -4
  54. package/src/migrations/vk_auth_003_role_scopes.sql +43 -0
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @venturekit/auth — Postgres VerificationCodeStore
3
+ *
4
+ * Production {@link VerificationCodeStore} backing the OTP primitives
5
+ * ({@link requestVerificationCode} / {@link verifyVerificationCode}).
6
+ * Talks to the project's primary Postgres database via
7
+ * `@venturekit/data`'s `query()` — or any `Querier`, so the caller can
8
+ * enlist code writes in an already-open transaction (pass a
9
+ * `Transaction`'s `query`; the default is the global connection pool).
10
+ *
11
+ * The backing table `vk_verification_codes` is shipped by this package's
12
+ * `migrations/vk_auth_001_verification_codes.sql` and applied by the
13
+ * in-VPC migration runner `@venturekit/infra` provisions — so consumers
14
+ * get the table and this store from the same package.
15
+ *
16
+ * `@venturekit/data` is an OPTIONAL peer dependency: importing THIS
17
+ * module is what opts a project into the Postgres store, so apps on a
18
+ * different backend (DynamoDB, the in-memory dev store) never load it
19
+ * and `@venturekit/auth/server` stays storage-agnostic.
20
+ *
21
+ * `expires_at` crosses the store boundary as ms-epoch to satisfy
22
+ * {@link VerificationCodeRecord}'s numeric contract: `to_timestamp` on
23
+ * write, `EXTRACT(EPOCH …) * 1000` on read.
24
+ */
25
+ import { query } from '@venturekit/data';
26
+ function rowToRecord(r) {
27
+ return {
28
+ channel: r.channel,
29
+ identifier: r.identifier,
30
+ codeHash: r.code_hash,
31
+ expiresAt: Number(r.expires_at),
32
+ attempts: r.attempts,
33
+ maxAttempts: r.max_attempts,
34
+ };
35
+ }
36
+ /**
37
+ * Build a Postgres-backed {@link VerificationCodeStore} bound to `q`.
38
+ *
39
+ * Pass a transaction's `query` (see `beginTransaction` /
40
+ * `withTransaction` in `@venturekit/data`) to make code writes
41
+ * participate in an open transaction; omit it to use the global pool.
42
+ */
43
+ export function createPostgresVerificationCodeStore(q = query) {
44
+ return {
45
+ async put({ channel, identifier, codeHash, expiresAt, maxAttempts }) {
46
+ // Opportunistic sweep of already-expired rows on every write —
47
+ // keeps the table bounded without a dedicated cron (the migration
48
+ // ships the `expires_at` index that backs this).
49
+ await q(`DELETE FROM vk_verification_codes WHERE expires_at < CURRENT_TIMESTAMP`);
50
+ // Idempotent per the store contract: a re-request overwrites the
51
+ // outstanding code for this (channel, identifier) and resets the
52
+ // attempt counter — a fresh code means "let me try again", not
53
+ // "reset the limit on the old one".
54
+ await q(`INSERT INTO vk_verification_codes
55
+ (channel, identifier, code_hash, expires_at, attempts, max_attempts)
56
+ VALUES ($1, $2, $3, to_timestamp($4 / 1000.0), 0, $5)
57
+ ON CONFLICT (channel, identifier) DO UPDATE
58
+ SET code_hash = EXCLUDED.code_hash,
59
+ expires_at = EXCLUDED.expires_at,
60
+ attempts = 0,
61
+ max_attempts = EXCLUDED.max_attempts,
62
+ created_at = NOW()`, [channel, identifier, codeHash, expiresAt, maxAttempts]);
63
+ },
64
+ async get({ channel, identifier }) {
65
+ // Return the row as-is, INCLUDING when expired — verifyVerificationCode
66
+ // performs the expiry check and deletes the row itself.
67
+ return q(`SELECT channel, identifier, code_hash,
68
+ (EXTRACT(EPOCH FROM expires_at) * 1000)::bigint AS expires_at,
69
+ attempts, max_attempts
70
+ FROM vk_verification_codes
71
+ WHERE channel = $1 AND identifier = $2
72
+ LIMIT 1`, [channel, identifier], (rows) => {
73
+ const r = rows[0];
74
+ return r ? rowToRecord(r) : undefined;
75
+ });
76
+ },
77
+ async incrementAttempts({ channel, identifier }) {
78
+ return q(`UPDATE vk_verification_codes
79
+ SET attempts = attempts + 1
80
+ WHERE channel = $1 AND identifier = $2
81
+ RETURNING attempts`, [channel, identifier], (rows) => rows[0]?.attempts ?? 0);
82
+ },
83
+ async delete({ channel, identifier }) {
84
+ await q(`DELETE FROM vk_verification_codes WHERE channel = $1 AND identifier = $2`, [channel, identifier]);
85
+ },
86
+ };
87
+ }
88
+ //# sourceMappingURL=postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../../src/server/store/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,KAAK,EAAgB,MAAM,kBAAkB,CAAC;AAiBvD,SAAS,WAAW,CAAC,CAAsB;IACzC,OAAO;QACL,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,QAAQ,EAAE,CAAC,CAAC,SAAS;QACrB,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;QAC/B,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,WAAW,EAAE,CAAC,CAAC,YAAY;KAC5B,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mCAAmC,CACjD,IAAa,KAAK;IAElB,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE;YACjE,+DAA+D;YAC/D,kEAAkE;YAClE,iDAAiD;YACjD,MAAM,CAAC,CACL,wEAAwE,CACzE,CAAC;YACF,iEAAiE;YACjE,iEAAiE;YACjE,+DAA+D;YAC/D,oCAAoC;YACpC,MAAM,CAAC,CACL;;;;;;;;qCAQ6B,EAC7B,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,CAAC,CACxD,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE;YAC/B,wEAAwE;YACxE,wDAAwD;YACxD,OAAO,CAAC,CACN;;;;;kBAKU,EACV,CAAC,OAAO,EAAE,UAAU,CAAC,EACrB,CAAC,IAAI,EAAE,EAAE;gBACP,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAA+C,CAAC;gBAChE,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACxC,CAAC,CACF,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,iBAAiB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE;YAC7C,OAAO,CAAC,CACN;;;6BAGqB,EACrB,CAAC,OAAO,EAAE,UAAU,CAAC,EACrB,CAAC,IAAI,EAAE,EAAE,CACN,IAAI,CAAC,CAAC,CAAsC,EAAE,QAAQ,IAAI,CAAC,CAC/D,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE;YAClC,MAAM,CAAC,CACL,0EAA0E,EAC1E,CAAC,OAAO,EAAE,UAAU,CAAC,CACtB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -8,8 +8,9 @@
8
8
  * treat those responses as fatal, we derive the remaining lifetime
9
9
  * from the access token itself.
10
10
  *
11
- * This file intentionally has no `@venturekit/auth/server` surface
12
- * exposure it's consumed by `sign-in.ts` and `refresh.ts` only.
11
+ * `decodeJwtClaims` IS part of the public surface: consumers that
12
+ * just minted tokens (sign-in, passwordless flows) read profile
13
+ * claims from the id token without a redundant verify round-trip.
13
14
  */
14
15
  /** Cognito's default access-token lifetime (1 hour). */
15
16
  export declare const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 3600;
@@ -24,4 +25,13 @@ export declare const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 3600;
24
25
  * `InitiateAuthCommand`). Use `verifyAndDecode` for untrusted input.
25
26
  */
26
27
  export declare function deriveExpiresInFromJwt(jwt: string, nowSeconds?: number): number;
28
+ /**
29
+ * Base64url-decode and JSON.parse a JWT payload. Returns `null` on any
30
+ * failure — callers fall back to a default.
31
+ *
32
+ * Does NOT verify the signature — only use on tokens received
33
+ * directly from the identity provider (e.g. right after sign-in).
34
+ * Use `verifyAndDecode` for anything that crossed a trust boundary.
35
+ */
36
+ export declare function decodeJwtClaims<T extends Record<string, unknown> = Record<string, unknown>>(jwt: string): T | null;
27
37
  //# sourceMappingURL=token-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"token-utils.d.ts","sourceRoot":"","sources":["../../src/server/token-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wDAAwD;AACxD,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAsC,GACjD,MAAM,CAgBR"}
1
+ {"version":3,"file":"token-utils.d.ts","sourceRoot":"","sources":["../../src/server/token-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,wDAAwD;AACxD,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAsC,GACjD,MAAM,CAgBR;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzF,GAAG,EAAE,MAAM,GACV,CAAC,GAAG,IAAI,CAYV"}
@@ -8,8 +8,9 @@
8
8
  * treat those responses as fatal, we derive the remaining lifetime
9
9
  * from the access token itself.
10
10
  *
11
- * This file intentionally has no `@venturekit/auth/server` surface
12
- * exposure it's consumed by `sign-in.ts` and `refresh.ts` only.
11
+ * `decodeJwtClaims` IS part of the public surface: consumers that
12
+ * just minted tokens (sign-in, passwordless flows) read profile
13
+ * claims from the id token without a redundant verify round-trip.
13
14
  */
14
15
  /** Cognito's default access-token lifetime (1 hour). */
15
16
  export const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 3600;
@@ -24,7 +25,7 @@ export const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 3600;
24
25
  * `InitiateAuthCommand`). Use `verifyAndDecode` for untrusted input.
25
26
  */
26
27
  export function deriveExpiresInFromJwt(jwt, nowSeconds = Math.floor(Date.now() / 1000)) {
27
- const payload = decodeJwtPayload(jwt);
28
+ const payload = decodeJwtClaims(jwt);
28
29
  if (!payload)
29
30
  return DEFAULT_ACCESS_TOKEN_TTL_SECONDS;
30
31
  const exp = typeof payload.exp === 'number' ? payload.exp : undefined;
@@ -42,8 +43,12 @@ export function deriveExpiresInFromJwt(jwt, nowSeconds = Math.floor(Date.now() /
42
43
  /**
43
44
  * Base64url-decode and JSON.parse a JWT payload. Returns `null` on any
44
45
  * failure — callers fall back to a default.
46
+ *
47
+ * Does NOT verify the signature — only use on tokens received
48
+ * directly from the identity provider (e.g. right after sign-in).
49
+ * Use `verifyAndDecode` for anything that crossed a trust boundary.
45
50
  */
46
- function decodeJwtPayload(jwt) {
51
+ export function decodeJwtClaims(jwt) {
47
52
  if (typeof jwt !== 'string' || jwt.length === 0)
48
53
  return null;
49
54
  const parts = jwt.split('.');
@@ -1 +1 @@
1
- {"version":3,"file":"token-utils.js","sourceRoot":"","sources":["../../src/server/token-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wDAAwD;AACxD,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;AAErD;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CACpC,GAAW,EACX,aAAqB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAElD,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,OAAO;QAAE,OAAO,gCAAgC,CAAC;IAEtD,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAEtE,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,gCAAgC,CAAC;IAE/D,MAAM,SAAS,GAAG,GAAG,GAAG,UAAU,CAAC;IACnC,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAEpC,0EAA0E;IAC1E,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,GAAG,GAAG;QAAE,OAAO,GAAG,GAAG,GAAG,CAAC;IAErD,OAAO,gCAAgC,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,GAAW;IACnC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACrE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC;QAC3C,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,GAA8B,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"token-utils.js","sourceRoot":"","sources":["../../src/server/token-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,wDAAwD;AACxD,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;AAErD;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CACpC,GAAW,EACX,aAAqB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAElD,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC,OAAO;QAAE,OAAO,gCAAgC,CAAC;IAEtD,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAEtE,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,gCAAgC,CAAC;IAE/D,MAAM,SAAS,GAAG,GAAG,GAAG,UAAU,CAAC;IACnC,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAEpC,0EAA0E;IAC1E,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,GAAG,GAAG;QAAE,OAAO,GAAG,GAAG,GAAG,CAAC;IAErD,OAAO,gCAAgC,CAAC;AAC1C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,GAAW;IAEX,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACrE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC;QAC3C,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,GAAQ,CAAC;QACpD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venturekit/auth",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Authentication and authorization for VentureKit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,24 +34,33 @@
34
34
  "./server": {
35
35
  "import": "./dist/server/index.js",
36
36
  "types": "./dist/server/index.d.ts"
37
+ },
38
+ "./server/store/postgres": {
39
+ "import": "./dist/server/store/postgres.js",
40
+ "types": "./dist/server/store/postgres.d.ts"
37
41
  }
38
42
  },
39
43
  "dependencies": {
40
- "@venturekit/core": "0.0.1",
44
+ "@venturekit/core": "0.0.2",
41
45
  "@aws-sdk/client-cognito-identity-provider": "^3.1068.0",
42
46
  "@aws-sdk/client-secrets-manager": "^3.1068.0",
43
47
  "aws-jwt-verify": "^4.0.1"
44
48
  },
45
49
  "peerDependencies": {
46
- "@venturekit/runtime": "0.0.1"
50
+ "@venturekit/data": "0.0.2",
51
+ "@venturekit/runtime": "0.0.2"
47
52
  },
48
53
  "peerDependenciesMeta": {
54
+ "@venturekit/data": {
55
+ "optional": true
56
+ },
49
57
  "@venturekit/runtime": {
50
58
  "optional": true
51
59
  }
52
60
  },
53
61
  "devDependencies": {
54
- "@venturekit/runtime": "0.0.1",
62
+ "@venturekit/data": "0.0.2",
63
+ "@venturekit/runtime": "0.0.2",
55
64
  "@types/aws-lambda": "^8.10.131",
56
65
  "@types/node": "^25.6.0",
57
66
  "typescript": "^5.3.0"
@@ -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.