@venturekit/auth 0.0.0-dev.20260701113915 → 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.
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/migrations/vk_auth_003_role_scopes.sql +43 -0
- package/dist/roles/index.d.ts +5 -1
- package/dist/roles/index.d.ts.map +1 -1
- package/dist/roles/index.js +4 -1
- package/dist/roles/index.js.map +1 -1
- package/dist/roles/role-scopes.d.ts +92 -0
- package/dist/roles/role-scopes.d.ts.map +1 -0
- package/dist/roles/role-scopes.js +122 -0
- package/dist/roles/role-scopes.js.map +1 -0
- package/dist/server/cookies.d.ts +77 -6
- package/dist/server/cookies.d.ts.map +1 -1
- package/dist/server/cookies.js +55 -13
- package/dist/server/cookies.js.map +1 -1
- package/dist/server/federated-routes.d.ts +29 -22
- package/dist/server/federated-routes.d.ts.map +1 -1
- package/dist/server/federated-routes.js +31 -4
- package/dist/server/federated-routes.js.map +1 -1
- package/dist/server/federated.d.ts.map +1 -1
- package/dist/server/federated.js +7 -11
- package/dist/server/federated.js.map +1 -1
- package/dist/server/handoff-routes.d.ts +130 -0
- package/dist/server/handoff-routes.d.ts.map +1 -0
- package/dist/server/handoff-routes.js +178 -0
- package/dist/server/handoff-routes.js.map +1 -0
- package/dist/server/handoff.d.ts +112 -0
- package/dist/server/handoff.d.ts.map +1 -0
- package/dist/server/handoff.js +102 -0
- package/dist/server/handoff.js.map +1 -0
- package/dist/server/index.d.ts +10 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +8 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/passwordless.d.ts +68 -0
- package/dist/server/passwordless.d.ts.map +1 -0
- package/dist/server/passwordless.js +136 -0
- package/dist/server/passwordless.js.map +1 -0
- package/dist/server/revoke.d.ts +10 -0
- package/dist/server/revoke.d.ts.map +1 -1
- package/dist/server/revoke.js +19 -1
- package/dist/server/revoke.js.map +1 -1
- package/dist/server/store/postgres.d.ts +35 -0
- package/dist/server/store/postgres.d.ts.map +1 -0
- package/dist/server/store/postgres.js +88 -0
- package/dist/server/store/postgres.js.map +1 -0
- package/dist/server/token-utils.d.ts +12 -2
- package/dist/server/token-utils.d.ts.map +1 -1
- package/dist/server/token-utils.js +9 -4
- package/dist/server/token-utils.js.map +1 -1
- package/package.json +13 -4
- 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
|
-
*
|
|
12
|
-
*
|
|
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
|
|
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
|
-
*
|
|
12
|
-
*
|
|
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 =
|
|
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
|
|
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
|
|
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.0-dev.
|
|
3
|
+
"version": "0.0.0-dev.20260704225856",
|
|
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.0-dev.
|
|
44
|
+
"@venturekit/core": "0.0.0-dev.20260704225856",
|
|
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/
|
|
50
|
+
"@venturekit/data": "0.0.0-dev.20260704225856",
|
|
51
|
+
"@venturekit/runtime": "0.0.0-dev.20260704225856"
|
|
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/
|
|
62
|
+
"@venturekit/data": "0.0.0-dev.20260704225856",
|
|
63
|
+
"@venturekit/runtime": "0.0.0-dev.20260704225856",
|
|
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.
|