@immediately-run/preauth-core 0.1.7 → 0.1.8

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.
@@ -13,6 +13,30 @@ export interface MintSentinels {
13
13
  * of a delegated grant's `parentGrantId`. `::` is delimiter-safe: `appKey` uses
14
14
  * `__` separators and a Firestore `spaceId` is alphanumeric. */
15
15
  export declare const grantKey: (appKey: string, spaceId: string) => string;
16
+ /** R3-98 S4 — the principal-aware grant key `(appKey, principal, spaceId)` (design
17
+ * 05a §3.1/§3.2). Additive: {@link grantKey} is retained for the legacy 2-field
18
+ * form. `::` stays delimiter-safe — `appKey` uses `__`, a `spaceId` is alphanumeric,
19
+ * and a named principal is lowercase-dotted/hyphenated (CA-3), none containing `::`. */
20
+ export declare const grantKeyWithPrincipal: (appKey: string, principal: string, spaceId: string) => string;
21
+ /** A parsed `parentGrantId` — the pieces the §8.15 revoke cascade reconstructs a
22
+ * grant doc path from. `principal` is present only for a 3-field (S4+) key. */
23
+ export interface ParsedGrantKey {
24
+ appKey: string;
25
+ spaceId: string;
26
+ /** The named principal for a 3-field {@link grantKeyWithPrincipal} key; undefined
27
+ * for a legacy 2-field {@link grantKey} (the caller defaults to its grandfather
28
+ * sentinel). */
29
+ principal?: string;
30
+ }
31
+ /** R3-98 S4 — ARITY-DETECTING parse of a grant key (design 05a §3.1 step 3 /
32
+ * MEDIUM-6). A 3-field key is `appKey::principal::spaceId`; a legacy 2-field key is
33
+ * `appKey::spaceId` (principal undefined). This lets the revoke cascade keep
34
+ * resolving BOTH legacy and keyed `parentGrantId`s after the re-key — a positional
35
+ * `split('::')` would mis-assign a legacy key's `spaceId` to `principal`. A
36
+ * malformed key (≠2/≠3 segments) degrades to best-effort `appKey::…::spaceId`
37
+ * (first + last), so the cascade fails safe (child self-revokes) rather than
38
+ * crashing. */
39
+ export declare const parseGrantKey: (key: string) => ParsedGrantKey;
16
40
  /** Durable elevated/app-scoped grants expire after 90 days WITHOUT USE; first
17
41
  * use after expiry re-prompts. Baseline needs no grant record, so this never
18
42
  * touches it. */
@@ -50,7 +74,7 @@ export declare const appKeyTouchFields: (s: MintSentinels) => Record<string, unk
50
74
  /** `user-app-spaces/{uid}/apps/{appKey}/spaces/{spaceId}` — the durable §8.7
51
75
  * grant doc (merge). `mintPath` defaults to `interactive`; `grantedAt`/`lastUsedAt`
52
76
  * drive the §8.15 90-day-unused expiry. */
53
- export declare const appSpaceGrantFields: (params: Pick<GrantSpaceParams, "name" | "subtree" | "mode" | "rules" | "declaredUri" | "mintPath" | "parentGrantId">, s: MintSentinels) => Record<string, unknown>;
77
+ export declare const appSpaceGrantFields: (params: Pick<GrantSpaceParams, "name" | "subtree" | "mode" | "rules" | "declaredUri" | "mintPath" | "parentGrantId" | "principal">, s: MintSentinels) => Record<string, unknown>;
54
78
  /** Union net:fetch host rules by origin (incoming wins) — the "consent
55
79
  * accumulates" merge both adapters apply before writing the host set. */
56
80
  export declare const mergeNetFetchHosts: (existing: readonly NetFetchHost[], incoming: readonly NetFetchHost[]) => NetFetchHost[];
package/dist/docLayout.js CHANGED
@@ -16,12 +16,35 @@
16
16
  // `.set()`/`.update()` is the only thing each adapter does itself. Drift is then
17
17
  // impossible without editing a helper both consume.
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.appCapabilitiesGrantFields = exports.mergeCapabilities = exports.netFetchGrantFields = exports.mergeNetFetchHosts = exports.appSpaceGrantFields = exports.appKeyTouchFields = exports.appCountFields = exports.userCountFields = exports.ownerUserSpaceFields = exports.ownerMemberFields = exports.spaceDocFields = exports.appCountPath = exports.userCountPath = exports.appSpacePath = exports.appKeyPath = exports.userSpacePath = exports.memberPath = exports.spacePath = exports.defined = exports.granteeId = exports.GRANT_EXPIRY_MS = exports.grantKey = void 0;
19
+ exports.appCapabilitiesGrantFields = exports.mergeCapabilities = exports.netFetchGrantFields = exports.mergeNetFetchHosts = exports.appSpaceGrantFields = exports.appKeyTouchFields = exports.appCountFields = exports.userCountFields = exports.ownerUserSpaceFields = exports.ownerMemberFields = exports.spaceDocFields = exports.appCountPath = exports.userCountPath = exports.appSpacePath = exports.appKeyPath = exports.userSpacePath = exports.memberPath = exports.spacePath = exports.defined = exports.granteeId = exports.GRANT_EXPIRY_MS = exports.parseGrantKey = exports.grantKeyWithPrincipal = exports.grantKey = void 0;
20
20
  /** Stable per-user identifier for a grant `(appKey, spaceId)`, used as the value
21
21
  * of a delegated grant's `parentGrantId`. `::` is delimiter-safe: `appKey` uses
22
22
  * `__` separators and a Firestore `spaceId` is alphanumeric. */
23
23
  const grantKey = (appKey, spaceId) => `${appKey}::${spaceId}`;
24
24
  exports.grantKey = grantKey;
25
+ /** R3-98 S4 — the principal-aware grant key `(appKey, principal, spaceId)` (design
26
+ * 05a §3.1/§3.2). Additive: {@link grantKey} is retained for the legacy 2-field
27
+ * form. `::` stays delimiter-safe — `appKey` uses `__`, a `spaceId` is alphanumeric,
28
+ * and a named principal is lowercase-dotted/hyphenated (CA-3), none containing `::`. */
29
+ const grantKeyWithPrincipal = (appKey, principal, spaceId) => `${appKey}::${principal}::${spaceId}`;
30
+ exports.grantKeyWithPrincipal = grantKeyWithPrincipal;
31
+ /** R3-98 S4 — ARITY-DETECTING parse of a grant key (design 05a §3.1 step 3 /
32
+ * MEDIUM-6). A 3-field key is `appKey::principal::spaceId`; a legacy 2-field key is
33
+ * `appKey::spaceId` (principal undefined). This lets the revoke cascade keep
34
+ * resolving BOTH legacy and keyed `parentGrantId`s after the re-key — a positional
35
+ * `split('::')` would mis-assign a legacy key's `spaceId` to `principal`. A
36
+ * malformed key (≠2/≠3 segments) degrades to best-effort `appKey::…::spaceId`
37
+ * (first + last), so the cascade fails safe (child self-revokes) rather than
38
+ * crashing. */
39
+ const parseGrantKey = (key) => {
40
+ const parts = key.split('::');
41
+ if (parts.length === 3) {
42
+ return { appKey: parts[0], principal: parts[1], spaceId: parts[2] };
43
+ }
44
+ // Legacy 2-field, or malformed → first segment is the appKey, last the spaceId.
45
+ return { appKey: parts[0], spaceId: parts[parts.length - 1] };
46
+ };
47
+ exports.parseGrantKey = parseGrantKey;
25
48
  /** Durable elevated/app-scoped grants expire after 90 days WITHOUT USE; first
26
49
  * use after expiry re-prompts. Baseline needs no grant record, so this never
27
50
  * touches it. */
@@ -123,6 +146,11 @@ const appSpaceGrantFields = (params, s) => (0, exports.defined)({
123
146
  grantedAt: s.serverTimestamp(),
124
147
  lastUsedAt: s.serverTimestamp(),
125
148
  name: params.name,
149
+ // R3-98 S3/S4 — the named principal this grant was minted under (design 05a
150
+ // §3.1). `defined()` omits it when absent, so a legacy/unkeyed mint writes no
151
+ // `principal` field and is grandfathered at the gate (both adapters stamp it
152
+ // identically, keeping the byte-identical-doc guarantee).
153
+ principal: params.principal,
126
154
  // UI_AS_APPS_SPEC §8.7: `rules` is authoritative; `subtree`/`mode` are kept as the
127
155
  // deprecated `rules[0]` mirror for not-yet-migrated readers. When no rule-set
128
156
  // is given, derive a single-rule set from the legacy scope so the backend
package/dist/port.d.ts CHANGED
@@ -60,6 +60,11 @@ export interface GrantSpaceParams {
60
60
  mintPath?: MintPath;
61
61
  /** §8.15 — parent `grantKey` for an M2 `delegated` grant. */
62
62
  parentGrantId?: string;
63
+ /** R3-98 S3/S4 — the **named principal** this grant is minted under (design 05a
64
+ * §3.1). Written as the `principal` field so the mount-admission gate re-checks
65
+ * it (a grant fires only under the principal it was minted with). Optional +
66
+ * additive: omitted ⇒ no field ⇒ a legacy/grandfathered grant. */
67
+ principal?: string;
63
68
  }
64
69
  /** Parameters for `MintStore.grantNetFetchHosts` — the per-(user, app) granted
65
70
  * host set, the grant half of the `manifest ∩ grant` net:fetch allowlist. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immediately-run/preauth-core",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "The shared §8.9 pre-auth target check + the single grant-mint path (mintConsentedGrants) + the capability vocabulary + the byte-faithful grant/space/net-fetch document layout. Consumed by site-main (browser Firestore) and the backend (admin Firestore) so there is ONE gate, ONE mint path, ONE wire layout.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {