@12-apps/prisma 6.6.0 → 6.7.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/prisma",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.7.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"description": "Prisma host: the multi-file schema folder, the plugin migration seam, and the shared PrismaClient singleton with its audit / append-only extensions",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@12-apps/audit": "^5.7.1",
|
|
71
|
-
"@12-apps/auth": "^2.
|
|
71
|
+
"@12-apps/auth": "^2.21.0",
|
|
72
72
|
"@12-apps/discounts": "^1.13.0",
|
|
73
73
|
"@12-apps/entitlements": "^3.10.0",
|
|
74
74
|
"@12-apps/entity-lifecycle": "^4.13.0",
|
|
@@ -76,14 +76,14 @@
|
|
|
76
76
|
"@12-apps/feature-flags": "^2.4.1",
|
|
77
77
|
"@12-apps/i18n": "^1.3.0",
|
|
78
78
|
"@12-apps/jobs": "^4.5.0",
|
|
79
|
-
"@12-apps/mcp": "^3.
|
|
80
|
-
"@12-apps/notifications": "^4.
|
|
79
|
+
"@12-apps/mcp": "^3.17.0",
|
|
80
|
+
"@12-apps/notifications": "^4.11.0",
|
|
81
81
|
"@12-apps/onboarding": "^2.7.0",
|
|
82
|
-
"@12-apps/payments-backend": "^4.
|
|
82
|
+
"@12-apps/payments-backend": "^4.27.1",
|
|
83
83
|
"@12-apps/product-research": "^2.10.0",
|
|
84
84
|
"@12-apps/rbac": "^4.14.0",
|
|
85
85
|
"@12-apps/realtime": "^2.6.0",
|
|
86
|
-
"@12-apps/report-builder": "^5.14.
|
|
86
|
+
"@12-apps/report-builder": "^5.14.1",
|
|
87
87
|
"@12-apps/shift": "^3.4.0",
|
|
88
88
|
"@12-apps/typescript-config": "^1.21.0",
|
|
89
89
|
"@types/node": "^22.10.6",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- @12-apps/mcp: the sealed successor that makes refresh rotation RETRYABLE.
|
|
2
|
+
--
|
|
3
|
+
-- `oauth_refresh_tokens.grace_seal` holds a token's own plaintext, encrypted
|
|
4
|
+
-- (AES-256-GCM) under a key derived by HKDF from the plaintext of the token it
|
|
5
|
+
-- was rotated FROM, with its grace deadline sealed inside the same blob.
|
|
6
|
+
--
|
|
7
|
+
-- WHY THE COLUMN EXISTS. Rotation-on-use with replay revocation cannot tell a
|
|
8
|
+
-- thief from a client that used one token twice for an innocent reason, and
|
|
9
|
+
-- there are two routine innocent reasons: a response lost to a proxy timeout,
|
|
10
|
+
-- and two of the client's own sessions refreshing at the same moment. Both were
|
|
11
|
+
-- punished as theft — the whole lineage revoked, including the successor just
|
|
12
|
+
-- handed to whoever won — which killed a live connection and sent a human back
|
|
13
|
+
-- through the full authorization flow. With this column, re-presenting a
|
|
14
|
+
-- just-consumed token inside the window returns THAT SAME successor instead, so
|
|
15
|
+
-- one successor is still all that ever exists and no second family is created.
|
|
16
|
+
--
|
|
17
|
+
-- WHY IT IS NOT A PLAINTEXT COLUMN. The key is never stored: it is derived from
|
|
18
|
+
-- the parent, which is itself only ever stored hashed. A dump of this table
|
|
19
|
+
-- therefore yields ciphertext and nothing that opens it, so the package's
|
|
20
|
+
-- "hashed, never plaintext" invariant is unchanged. The only party that can open
|
|
21
|
+
-- a seal is one presenting the parent — which is the party being served, and
|
|
22
|
+
-- which already held the token that mints the successor.
|
|
23
|
+
--
|
|
24
|
+
-- The seal is CLEARED whenever a token is consumed or revoked, and that is the
|
|
25
|
+
-- bound on what it costs: a seal opens only under the plaintext it was rotated
|
|
26
|
+
-- from, so spent seals left in place would chain — one historical plaintext plus
|
|
27
|
+
-- a copy of this table would walk forward to the live token offline, with no
|
|
28
|
+
-- server call and therefore no replay detection.
|
|
29
|
+
--
|
|
30
|
+
-- Nullable, but NOT optional: the package writes this field on every rotation,
|
|
31
|
+
-- so a deployment that raises the package version without applying this
|
|
32
|
+
-- migration gets a runtime failure on every refresh, not a quietly disabled
|
|
33
|
+
-- window. Apply it in the same change as the version raise. Guarded with
|
|
34
|
+
-- IF NOT EXISTS like every other statement this package ships, so a host that
|
|
35
|
+
-- already added the column adopts the migration as a no-op.
|
|
36
|
+
ALTER TABLE "oauth_refresh_tokens"
|
|
37
|
+
ADD COLUMN IF NOT EXISTS "grace_seal" TEXT;
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"20260821140000_discounts_package_owned",
|
|
39
39
|
"20260824120000_payments_pending_verification",
|
|
40
40
|
"20260826170000_locale_preferences",
|
|
41
|
-
"20260830120000_discount_weekly_schedule"
|
|
41
|
+
"20260830120000_discount_weekly_schedule",
|
|
42
|
+
"20260910120000_add_refresh_grace_seal"
|
|
42
43
|
]
|
|
43
44
|
}
|
package/prisma/schema/mcp.prisma
CHANGED
|
@@ -71,6 +71,16 @@ model OAuthRefreshToken {
|
|
|
71
71
|
revokedAt DateTime? @map("revoked_at")
|
|
72
72
|
createdAt DateTime @default(now()) @map("created_at")
|
|
73
73
|
|
|
74
|
+
// This token's own plaintext, SEALED under a key derived from the plaintext of
|
|
75
|
+
// the token it was rotated from, with its grace deadline inside the sealed
|
|
76
|
+
// blob. It is what lets a rotation be RETRIED: re-presenting the consumed
|
|
77
|
+
// parent within the window returns this same successor instead of revoking the
|
|
78
|
+
// lineage, so a lost response or two concurrent refreshes no longer end the
|
|
79
|
+
// connection. Only a caller holding the parent can open it, so the "never store
|
|
80
|
+
// a token in the clear" rule is intact — a dump of this table yields ciphertext
|
|
81
|
+
// and no key. Null on a root token, and whenever the window is turned off.
|
|
82
|
+
graceSeal String? @map("grace_seal")
|
|
83
|
+
|
|
74
84
|
// `tokenHash` is already indexed by its `@unique` constraint — the
|
|
75
85
|
// lookup-on-presentation path — so no separate `@@index([tokenHash])` is added
|
|
76
86
|
// (it would be redundant). The composite index serves per-user/per-client
|