@vantageos/cloud-identity 0.4.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -3,6 +3,75 @@
3
3
  All notable changes to `@vantageos/cloud-identity` are documented here.
4
4
  This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## [0.6.0]
7
+
8
+ **Additive — typed refusal for reads, and a role assertion.**
9
+
10
+ ### Added
11
+
12
+ - `resolveTenantIdOrAbsent(source)` (`src/org-guard.ts`): non-throwing sibling
13
+ of `requireTenantId`. Returns `{ present: true, tenantId }` or a typed
14
+ `{ present: false, absence: { code: "TENANT_ABSENT", reason } }`. A read that
15
+ throws crashes a mounted render; a read that returns a bare empty value is
16
+ byte-identical to "there is nothing". This returns neither.
17
+ - `requireHumanRole(session, required)` and
18
+ `resolveHumanRoleOrRefusal(session, required)` (`src/human-path.ts`): role
19
+ assertion on the verified session's `orgRole`, throwing and non-throwing
20
+ forms. Exact match, no hierarchy; an empty required list admits nobody.
21
+ - Types: `TenantAbsence`, `TenantAbsenceReason`, `TenantResolution`,
22
+ `RequiredHumanRole`, `RoleRefusal`, `RoleRefusalReason`, `RoleResolution`.
23
+
24
+ ### Fixed
25
+
26
+ - The Clerk org-role map is now prototype-less (`Object.create(null)`). As a
27
+ plain object literal it inherited `Object.prototype`, so a presented
28
+ `orgRole` of `toString`, `constructor`, `valueOf` or `__proto__` did not
29
+ MISS the lookup — it returned an inherited value, which is truthy, and the
30
+ resolver handed back a FUNCTION as the role instead of refusing. Any key
31
+ that is not one of the eight own keys is now `undefined`, so the existing
32
+ refusal path rejects it. Fixed at the data; no lookup expression and no
33
+ function body changed, and no list of forbidden names was introduced.
34
+
35
+ ### Unchanged
36
+
37
+ - `requireTenantId` and `normalizeVerifiedHumanSession` are byte-for-byte
38
+ unchanged and still throw on every refusal.
39
+ - The tenant key stays opaque: no export takes or returns a slug. That
40
+ opacity is now pinned on all four served paths (session, bearer,
41
+ self-host, `role.identity.tenant`), through BOTH the throwing and the
42
+ non-throwing sibling, with a fixture carrying edge whitespace so that
43
+ splicing a `.trim()` onto a served id goes RED instead of passing.
44
+
45
+ ### Removed
46
+
47
+ - `bun.lock`, committed by accident in 0.5.0. `package-lock.json` is the
48
+ lockfile of record; two lockfiles for two package managers guarantee a
49
+ silent divergence.
50
+
51
+ ## [0.5.0]
52
+
53
+ **Additive — grant-aware scope filter.** Closes the other pole of the
54
+ fail-closed defect class: 0.3.0/0.4.0 closed "a right granted by ABSENCE"
55
+ (missing `oauthCtx` silently passing everything); this closes "a right
56
+ REFUSED by absence" — a per-row grant (a named mission agent/pilot, a
57
+ mandate's `fulfilledBy`) that IS present on the row but that
58
+ `passesScopeFilter`/`scopeFilterList`/`scopeFilterGet` were structurally
59
+ blind to, because they only ever consulted `createdBy`/`namespace`.
60
+
61
+ ### Added
62
+
63
+ - `passesScopeFilter`, `scopeFilterList`, `scopeFilterGet` (`src/scope-filter.ts`)
64
+ now accept an optional `grantFields: readonly string[]` parameter, defaulting
65
+ to `[]`. Each entry names a property on the row that the CALLER declares as
66
+ a per-row grant — the field's runtime value is treated as a single identity
67
+ (`string`, equality match) or multiple identities (`string[]`, any-element
68
+ match) against `oauthCtx.fromAllowList`. The package never hardcodes which
69
+ fields are grants for which table; that declaration is caller-supplied data.
70
+ - **Fail-closed regression preserved byte-for-byte:** omitting `grantFields`
71
+ (or passing `[]`) reproduces 0.4.0 behaviour exactly — this is a pure
72
+ widening, no existing caller's behaviour changes without an explicit
73
+ opt-in declaration at its own call site.
74
+
6
75
  ## [0.4.0]
7
76
 
8
77
  **Additive.** Two new primitives so one identity layer covers both deployment
package/README.md CHANGED
@@ -105,6 +105,27 @@ await resolveTenant({ session: null }); // throws Error("Unauthenticated: no ses
105
105
  `{ kind: "bearer", context }` for a machine caller. A missing session, a
106
106
  missing organization and an empty organization id are all refusals, never a
107
107
  default.
108
+ - `resolveTenantIdOrAbsent(source)` — the NON-throwing sibling of
109
+ `requireTenantId`, for a public READ. Same `TenantSource` union, same
110
+ refusal conditions; instead of throwing it returns
111
+ `{ present: true, tenantId }` or
112
+ `{ present: false, absence: { code: "TENANT_ABSENT", reason } }`
113
+ (`TenantAbsence`, `TenantAbsenceReason`, `TenantResolution`). The absence
114
+ is a non-empty object: it is never `[]`, `{}`, `null` or a zeroed count, so
115
+ "no organization" and "an organization with nothing in it" cannot collapse
116
+ into one value. Use `requireTenantId` at a WRITE boundary and
117
+ `resolveTenantIdOrAbsent` where a throw would crash a mounted render. The
118
+ tenant id is OPAQUE — returned byte-for-byte, never a slug or display name.
119
+ - `requireHumanRole(session, required)` — asserts the VERIFIED session
120
+ carries `required` (a `HumanAccountRole` or a list of them, exact match, no
121
+ hierarchy) and returns the resolved identity; throws otherwise. The role is
122
+ read from `session.orgRole`, never from an argument. `RequiredHumanRole`
123
+ is its `required` type.
124
+ - `resolveHumanRoleOrRefusal(session, required)` — the non-throwing form:
125
+ `{ admitted: true, identity }` or
126
+ `{ admitted: false, refusal: { code: "ROLE_REFUSED", reason } }`
127
+ (`RoleResolution`, `RoleRefusal`, `RoleRefusalReason`). `reason` is
128
+ `role-not-held` for a verified member lacking the role.
108
129
  - `getEffectiveTenantId(ctx, args)` — resolves which tenant a request should
109
130
  act on when a caller may legitimately act on more than one.
110
131
 
@@ -92,4 +92,50 @@ export type ResolvedHumanIdentity = {
92
92
  * ```
93
93
  */
94
94
  export declare function normalizeVerifiedHumanSession(session: ClerkSessionLike | null | undefined): ResolvedHumanIdentity;
95
+ /**
96
+ * Why a caller was not admitted. `role-not-held` is the one that matters: a
97
+ * verified member of the organization who does not carry the required role.
98
+ */
99
+ export type RoleRefusalReason = "no-session" | "no-organization" | "no-verified-role" | "role-not-held";
100
+ /** The typed refusal. A non-empty object carrying its own code. */
101
+ export type RoleRefusal = {
102
+ code: "ROLE_REFUSED";
103
+ reason: RoleRefusalReason;
104
+ };
105
+ /** Result of `resolveHumanRoleOrRefusal`. Branch on `admitted`. */
106
+ export type RoleResolution = {
107
+ admitted: true;
108
+ identity: ResolvedHumanIdentity;
109
+ } | {
110
+ admitted: false;
111
+ refusal: RoleRefusal;
112
+ };
113
+ /**
114
+ * `required` is the POLICY the call site declares; the caller's role is the
115
+ * CLAIM, and it is read only from `session.orgRole` (which the caller has
116
+ * already verified upstream — see the @security note on
117
+ * `normalizeVerifiedHumanSession`). There is no parameter through which a
118
+ * caller can supply its own role. Matching is EXACT, with no hierarchy:
119
+ * `owner` does not satisfy `"admin"`; pass `["owner", "admin"]` to admit
120
+ * either. An empty list admits nobody.
121
+ */
122
+ export type RequiredHumanRole = HumanAccountRole | readonly HumanAccountRole[];
123
+ /**
124
+ * THROWING form, for a write boundary. Returns the resolved identity when
125
+ * the verified session carries one of the `required` roles; throws
126
+ * otherwise. Malformed sessions throw exactly what
127
+ * `normalizeVerifiedHumanSession` throws; a well-formed session lacking the
128
+ * role throws "Role refused: ...".
129
+ *
130
+ * Same `@security` precondition as `normalizeVerifiedHumanSession`: this
131
+ * asserts on a session that is ALREADY verified. It verifies nothing.
132
+ */
133
+ export declare function requireHumanRole(session: ClerkSessionLike | null | undefined, required: RequiredHumanRole): ResolvedHumanIdentity;
134
+ /**
135
+ * NON-THROWING form, for a public read. Returns `{ admitted: true, identity }`
136
+ * or `{ admitted: false, refusal }` where the refusal carries `ROLE_REFUSED`
137
+ * and a reason. Admits exactly the sessions `requireHumanRole` returns for
138
+ * and refuses exactly the ones it throws for.
139
+ */
140
+ export declare function resolveHumanRoleOrRefusal(session: ClerkSessionLike | null | undefined, required: RequiredHumanRole): RoleResolution;
95
141
  //# sourceMappingURL=human-path.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"human-path.d.ts","sourceRoot":"","sources":["../src/human-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,sBAAsB;;;;;EAKjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAMtE;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAMF,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AAoBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAC3C,qBAAqB,CAiCvB"}
1
+ {"version":3,"file":"human-path.d.ts","sourceRoot":"","sources":["../src/human-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,sBAAsB;;;;;EAKjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAMtE;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAMF,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AAgCF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAC3C,qBAAqB,CAiCvB;AAMD;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,iBAAiB,GACjB,kBAAkB,GAClB,eAAe,CAAC;AAEpB,mEAAmE;AACnE,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAEF,mEAAmE;AACnE,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,qBAAqB,CAAA;CAAE,GACnD;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG,SAAS,gBAAgB,EAAE,CAAC;AAM/E;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC5C,QAAQ,EAAE,iBAAiB,GAC1B,qBAAqB,CAWvB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC5C,QAAQ,EAAE,iBAAiB,GAC1B,cAAc,CAqBhB"}
@@ -35,7 +35,17 @@ export const humanAccountRoleSchema = z.enum([
35
35
  * listed here is refused rather than silently coerced to a default, per
36
36
  * "presented, never inferred from an absence."
37
37
  */
38
- const CLERK_ORG_ROLE_MAP = {
38
+ const CLERK_ORG_ROLE_MAP = Object.assign(
39
+ // PROTOTYPE-LESS on purpose. As a plain object literal this map INHERITED
40
+ // `Object.prototype`, so a lookup of a presented role like `toString`,
41
+ // `constructor` or `__proto__` did not MISS — it returned an inherited
42
+ // value, which is truthy, and the resolver handed back a FUNCTION as the
43
+ // role instead of refusing. With a null prototype, any key that is not one
44
+ // of the eight own keys below is `undefined`, so the existing
45
+ // `if (!mapped) throw` refuses it. The lookup expressions in
46
+ // `normalizeVerifiedHumanSession` and `resolveHumanRoleOrRefusal` are
47
+ // unchanged — this is a fix at the DATA, not a list of forbidden names.
48
+ Object.create(null), {
39
49
  "org:owner": "owner",
40
50
  owner: "owner",
41
51
  "org:admin": "admin",
@@ -44,7 +54,7 @@ const CLERK_ORG_ROLE_MAP = {
44
54
  member: "member",
45
55
  "org:client": "client",
46
56
  client: "client",
47
- };
57
+ });
48
58
  /**
49
59
  * @security ⚠️ DECODE/NORMALIZE-ONLY. NOT AUTHENTICATION. NOT VERIFICATION.
50
60
  *
@@ -114,4 +124,55 @@ export function normalizeVerifiedHumanSession(session) {
114
124
  }
115
125
  return { tenant: orgId, subject: userId, role: mapped };
116
126
  }
127
+ function requiredList(required) {
128
+ return typeof required === "string" ? [required] : required;
129
+ }
130
+ /**
131
+ * THROWING form, for a write boundary. Returns the resolved identity when
132
+ * the verified session carries one of the `required` roles; throws
133
+ * otherwise. Malformed sessions throw exactly what
134
+ * `normalizeVerifiedHumanSession` throws; a well-formed session lacking the
135
+ * role throws "Role refused: ...".
136
+ *
137
+ * Same `@security` precondition as `normalizeVerifiedHumanSession`: this
138
+ * asserts on a session that is ALREADY verified. It verifies nothing.
139
+ */
140
+ export function requireHumanRole(session, required) {
141
+ const identity = normalizeVerifiedHumanSession(session);
142
+ const allowed = requiredList(required);
143
+ if (!allowed.includes(identity.role)) {
144
+ throw new Error(`Role refused: this session is a verified member but does not carry ${allowed.length === 0 ? "any admitted role" : `role ${allowed.join(" or ")}`}.`);
145
+ }
146
+ return identity;
147
+ }
148
+ /**
149
+ * NON-THROWING form, for a public read. Returns `{ admitted: true, identity }`
150
+ * or `{ admitted: false, refusal }` where the refusal carries `ROLE_REFUSED`
151
+ * and a reason. Admits exactly the sessions `requireHumanRole` returns for
152
+ * and refuses exactly the ones it throws for.
153
+ */
154
+ export function resolveHumanRoleOrRefusal(session, required) {
155
+ const refuse = (reason) => ({
156
+ admitted: false,
157
+ refusal: { code: "ROLE_REFUSED", reason },
158
+ });
159
+ if (!session)
160
+ return refuse("no-session");
161
+ const { orgId, userId, orgRole } = session;
162
+ if (typeof orgId !== "string" || orgId.length === 0) {
163
+ return refuse("no-organization");
164
+ }
165
+ if (typeof userId !== "string" || userId.length === 0) {
166
+ return refuse("no-verified-role");
167
+ }
168
+ if (typeof orgRole !== "string" || orgRole.length === 0) {
169
+ return refuse("no-verified-role");
170
+ }
171
+ const role = CLERK_ORG_ROLE_MAP[orgRole];
172
+ if (!role)
173
+ return refuse("no-verified-role");
174
+ if (!requiredList(required).includes(role))
175
+ return refuse("role-not-held");
176
+ return { admitted: true, identity: { tenant: orgId, subject: userId, role } };
177
+ }
117
178
  //# sourceMappingURL=human-path.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"human-path.js","sourceRoot":"","sources":["../src/human-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC;IAC3C,OAAO;IACP,OAAO;IACP,QAAQ;IACR,QAAQ;CACT,CAAC,CAAC;AA6BH;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAqC;IAC3D,WAAW,EAAE,OAAO;IACpB,KAAK,EAAE,OAAO;IACd,WAAW,EAAE,OAAO;IACpB,KAAK,EAAE,OAAO;IACd,YAAY,EAAE,QAAQ;IACtB,MAAM,EAAE,QAAQ;IAChB,YAAY,EAAE,QAAQ;IACtB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,UAAU,6BAA6B,CAC3C,OAA4C;IAE5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAE3C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IAED,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CACb,0FAA0F,CAC3F,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,mCAAmC,OAAO,sBAAsB,MAAM,CAAC,IAAI,CACzE,kBAAkB,CACnB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAChB,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1D,CAAC"}
1
+ {"version":3,"file":"human-path.js","sourceRoot":"","sources":["../src/human-path.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -11,8 +11,8 @@ export { validateMasterBearer } from "./bearer-validation.js";
11
11
  export type { OAuthCtx, ScopeProfile, NamespacePrefix, FromAllowListEntry, ValidateMasterBearerResult, } from "./types.js";
12
12
  export { workspaceSchema, workspaceMemberSchema, workspaceRoleSchema, tenantContextSchema, ScopeViolationError, getEffectiveTenantId, decodeUnverifiedBearer, } from "./tenancy-domain.js";
13
13
  export type { Workspace, WorkspaceMember, WorkspaceRole, TenantContext, ScopeViolationPayload, BearerPayload, } from "./tenancy-domain.js";
14
- export { requireTenantId } from "./org-guard.js";
15
- export type { SessionIdentity, TenantSource, DeploymentMode, } from "./org-guard.js";
16
- export { humanAccountRoleSchema, normalizeVerifiedHumanSession } from "./human-path.js";
17
- export type { HumanAccountRole, ClerkSessionLike, ResolvedHumanIdentity, } from "./human-path.js";
14
+ export { requireTenantId, resolveTenantIdOrAbsent } from "./org-guard.js";
15
+ export type { TenantAbsence, TenantAbsenceReason, TenantResolution, SessionIdentity, TenantSource, DeploymentMode, } from "./org-guard.js";
16
+ export { humanAccountRoleSchema, normalizeVerifiedHumanSession, requireHumanRole, resolveHumanRoleOrRefusal, } from "./human-path.js";
17
+ export type { RequiredHumanRole, RoleRefusal, RoleRefusalReason, RoleResolution, HumanAccountRole, ClerkSessionLike, ResolvedHumanIdentity, } from "./human-path.js";
18
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EACV,eAAe,EACf,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAC;AACxF,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ export { validateMasterBearer } from "./bearer-validation.js";
11
11
  // 0.2.0 — Domain-tenancy layer (additive)
12
12
  export { workspaceSchema, workspaceMemberSchema, workspaceRoleSchema, tenantContextSchema, ScopeViolationError, getEffectiveTenantId, decodeUnverifiedBearer, } from "./tenancy-domain.js";
13
13
  // Organization-membership guard (unified session + bearer + self-host contract)
14
- export { requireTenantId } from "./org-guard.js";
14
+ export { requireTenantId, resolveTenantIdOrAbsent } from "./org-guard.js";
15
15
  // 0.4.0 — Human-path resolver (Clerk-shaped session -> { tenant, subject, role })
16
- export { humanAccountRoleSchema, normalizeVerifiedHumanSession } from "./human-path.js";
16
+ export { humanAccountRoleSchema, normalizeVerifiedHumanSession, requireHumanRole, resolveHumanRoleOrRefusal, } from "./human-path.js";
17
17
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAS9D,0CAA0C;AAC1C,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAU7B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAOjD,kFAAkF;AAClF,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAS9D,0CAA0C;AAC1C,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAU7B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAU1E,kFAAkF;AAClF,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC"}
@@ -92,4 +92,52 @@ export type TenantSource = {
92
92
  * organization/workspace information is missing.
93
93
  */
94
94
  export declare function requireTenantId(source: TenantSource): string;
95
+ /**
96
+ * Why a caller resolved to no tenant. A closed set, so a consumer can branch
97
+ * on content.
98
+ */
99
+ export type TenantAbsenceReason = "no-session" | "no-organization" | "no-workspace" | "no-tenant-configured";
100
+ /**
101
+ * The typed absence. It is a NON-empty object carrying its own code: it can
102
+ * never be mistaken for `[]`, `{}`, `null`, `0` or a zeroed aggregate, all of
103
+ * which are what "an organization with nothing in it" looks like.
104
+ */
105
+ export type TenantAbsence = {
106
+ code: "TENANT_ABSENT";
107
+ reason: TenantAbsenceReason;
108
+ };
109
+ /**
110
+ * Result of `resolveTenantIdOrAbsent`. Branch on `present`; the two arms
111
+ * share no field, so a consumer that forgets to branch gets a type error
112
+ * rather than a silently-empty read.
113
+ */
114
+ export type TenantResolution = {
115
+ present: true;
116
+ tenantId: string;
117
+ } | {
118
+ present: false;
119
+ absence: TenantAbsence;
120
+ };
121
+ /**
122
+ * The tenant id is OPAQUE. It is returned byte-for-byte as presented
123
+ * (`orgId`, `workspaceId` or the self-host `tenantId`), never trimmed,
124
+ * lower-cased or otherwise interpreted. This package never receives or
125
+ * derives a slug or any human-facing name; each product maps its own notion
126
+ * (an organization id, a workspace id) onto that string.
127
+ *
128
+ * NON-THROWING sibling of `requireTenantId`, for a public READ.
129
+ *
130
+ * `requireTenantId` throws on every branch, which is right for a WRITE
131
+ * boundary and wrong for a subscribed read: a throw at a caller with no
132
+ * organization crashes a mounted render, while a bare empty value is
133
+ * byte-identical to "there is nothing". This function does neither: it
134
+ * returns the tenant, or a typed absence that cannot be confused with an
135
+ * empty result.
136
+ *
137
+ * Same `TenantSource` union, same refusal conditions as `requireTenantId` —
138
+ * every input that makes it throw makes this return `{ present: false }`,
139
+ * and every input it serves is served identically. Grants nothing that
140
+ * `requireTenantId` refuses.
141
+ */
142
+ export declare function resolveTenantIdOrAbsent(source: TenantSource): TenantResolution;
95
143
  //# sourceMappingURL=org-guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"org-guard.d.ts","sourceRoot":"","sources":["../src/org-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,WAAW,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,CAAC;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA+B5D"}
1
+ {"version":3,"file":"org-guard.d.ts","sourceRoot":"","sources":["../src/org-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,WAAW,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,CAAC;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA+B5D;AAMD;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAC3B,YAAY,GACZ,iBAAiB,GACjB,cAAc,GACd,sBAAsB,CAAC;AAE3B;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,mBAAmB,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,CA4B9E"}
package/dist/org-guard.js CHANGED
@@ -74,4 +74,52 @@ export function requireTenantId(source) {
74
74
  }
75
75
  return workspaceId;
76
76
  }
77
+ /**
78
+ * The tenant id is OPAQUE. It is returned byte-for-byte as presented
79
+ * (`orgId`, `workspaceId` or the self-host `tenantId`), never trimmed,
80
+ * lower-cased or otherwise interpreted. This package never receives or
81
+ * derives a slug or any human-facing name; each product maps its own notion
82
+ * (an organization id, a workspace id) onto that string.
83
+ *
84
+ * NON-THROWING sibling of `requireTenantId`, for a public READ.
85
+ *
86
+ * `requireTenantId` throws on every branch, which is right for a WRITE
87
+ * boundary and wrong for a subscribed read: a throw at a caller with no
88
+ * organization crashes a mounted render, while a bare empty value is
89
+ * byte-identical to "there is nothing". This function does neither: it
90
+ * returns the tenant, or a typed absence that cannot be confused with an
91
+ * empty result.
92
+ *
93
+ * Same `TenantSource` union, same refusal conditions as `requireTenantId` —
94
+ * every input that makes it throw makes this return `{ present: false }`,
95
+ * and every input it serves is served identically. Grants nothing that
96
+ * `requireTenantId` refuses.
97
+ */
98
+ export function resolveTenantIdOrAbsent(source) {
99
+ const absent = (reason) => ({
100
+ present: false,
101
+ absence: { code: "TENANT_ABSENT", reason },
102
+ });
103
+ if (source.kind === "session") {
104
+ if (!source.identity)
105
+ return absent("no-session");
106
+ const orgId = source.identity.orgId;
107
+ if (typeof orgId !== "string" || orgId.length === 0) {
108
+ return absent("no-organization");
109
+ }
110
+ return { present: true, tenantId: orgId };
111
+ }
112
+ if (source.kind === "self-host") {
113
+ const tenantId = source.tenantId;
114
+ if (typeof tenantId !== "string" || tenantId.length === 0) {
115
+ return absent("no-tenant-configured");
116
+ }
117
+ return { present: true, tenantId };
118
+ }
119
+ const workspaceId = source.context?.workspaceId;
120
+ if (typeof workspaceId !== "string" || workspaceId.length === 0) {
121
+ return absent("no-workspace");
122
+ }
123
+ return { present: true, tenantId: workspaceId };
124
+ }
77
125
  //# sourceMappingURL=org-guard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"org-guard.js","sourceRoot":"","sources":["../src/org-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAyCH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1D,MAAM,IAAI,KAAK,CACb,gHAAgH,CACjH,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC;IAChD,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CACb,0GAA0G,CAC3G,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC"}
1
+ {"version":3,"file":"org-guard.js","sourceRoot":"","sources":["../src/org-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAyCH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1D,MAAM,IAAI,KAAK,CACb,gHAAgH,CACjH,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC;IAChD,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CACb,0GAA0G,CAC3G,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAmCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAoB;IAC1D,MAAM,MAAM,GAAG,CAAC,MAA2B,EAAoB,EAAE,CAAC,CAAC;QACjE,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE;KAC3C,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ;YAAE,OAAO,MAAM,CAAC,YAAY,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpD,OAAO,MAAM,CAAC,iBAAiB,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1D,OAAO,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACrC,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC;IAChD,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,MAAM,CAAC,cAAc,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AAClD,CAAC"}
@@ -5,6 +5,31 @@
5
5
  * implementation; the only change was replacing a framework-specific context
6
6
  * type with the framework-agnostic `OAuthCtx` from `./types`.
7
7
  *
8
+ * Contract (0.5.0 — additive, widening only, vs. 0.4.0):
9
+ * - Every exported predicate/filter now accepts an OPTIONAL `grantFields`
10
+ * parameter: `readonly string[]`, defaulting to `[]`. Each entry names a
11
+ * property on the row that the CALLER declares as a per-row grant (e.g.
12
+ * "agents", "pilot", "fulfilledBy"). The package never hardcodes which
13
+ * fields are grants for which table — that declaration is data supplied
14
+ * by the caller at the call site, not business knowledge baked into this
15
+ * shared module (no-hardcoded-business-knowledge).
16
+ * - A row passes for a non-master caller if createdBy/namespace already
17
+ * passed (0.4.0 behaviour, unchanged) OR if ANY declared grant field's
18
+ * value names an identity in `oauthCtx.fromAllowList` — the field may be
19
+ * a single string (e.g. "pilot") or a string[] (e.g. "agents"): a string
20
+ * value matches by equality, an array value matches if any element is in
21
+ * `fromAllowList`.
22
+ * - `grantFields` defaulting to `[]` means a caller that declares NO grants
23
+ * gets EXACTLY the 0.4.0 behaviour, byte-for-byte — this is a pure
24
+ * widening, no existing caller's behaviour changes without an explicit
25
+ * opt-in declaration at its own call site.
26
+ * - This closes the OTHER pole of the fail-closed defect: 0.3.0/0.4.0
27
+ * closed "a right granted by ABSENCE" (missing oauthCtx silently passing
28
+ * everything); this closes "a right REFUSED by absence" — a grant that
29
+ * IS present on the row (a named agent, pilot, or fulfiller) but that the
30
+ * filter was structurally blind to because it only ever consulted
31
+ * createdBy/namespace.
32
+ *
8
33
  * Contract (0.3.0 — BREAKING vs. 0.2.0):
9
34
  * - `oauthCtx` is now MANDATORY. A right is never granted by absence: the
10
35
  * 0.2.0 behaviour of `oauthCtx === undefined` silently passing everything
@@ -50,6 +75,18 @@ export type ScopeFilterable = {
50
75
  * `fromAllowList` contains the `"*"` sentinel.
51
76
  */
52
77
  export declare function isMasterScope(ctx: OAuthCtx): boolean;
78
+ /**
79
+ * Names of row properties the CALLER declares as per-row grants. Each named
80
+ * property's runtime value must be a `string` (single identity) or
81
+ * `string[]` (multiple identities) to be consulted — any other runtime shape
82
+ * is silently skipped (not a match), never thrown, since these are duck-typed
83
+ * against arbitrary Convex documents.
84
+ *
85
+ * This is DATA supplied by the call site, never a list hardcoded inside this
86
+ * package: the package does not and must not know that "agents" means
87
+ * missions or "fulfilledBy" means mandates.
88
+ */
89
+ export type GrantFieldDeclaration = readonly string[];
53
90
  /**
54
91
  * Core predicate. Returns true when the row is visible to the caller.
55
92
  *
@@ -57,6 +94,9 @@ export declare function isMasterScope(ctx: OAuthCtx): boolean;
57
94
  * - row.createdBy ∈ oauthCtx.fromAllowList → true
58
95
  * - row.namespace === prefix OR startsWith prefix+'/'
59
96
  * for any prefix ∈ oauthCtx.namespaceReadPrefixes → true
97
+ * - row[grantField] (for any declared grantField) names an identity in
98
+ * oauthCtx.fromAllowList, where the field is a string (equality) or a
99
+ * string[] (any-element membership) → true
60
100
  * - otherwise → false
61
101
  *
62
102
  * `oauthCtx` is mandatory (see module-level contract note above): an omitted
@@ -64,21 +104,32 @@ export declare function isMasterScope(ctx: OAuthCtx): boolean;
64
104
  * need the old legacy-bearer wildcard behaviour must pass `LEGACY_WILDCARD_CTX`
65
105
  * explicitly.
66
106
  *
107
+ * `grantFields` (0.5.0, optional, defaults to `[]`) lets a caller widen the
108
+ * predicate for its own table without this package hardcoding which fields
109
+ * are grants — see the module-level 0.5.0 contract note above. Omitting it
110
+ * (or passing `[]`) reproduces 0.4.0 behaviour exactly.
111
+ *
67
112
  * Substring matches that don't fall on a '/' boundary are explicitly rejected
68
113
  * (e.g. namespace="orchestrator/alphabet" does NOT match prefix
69
114
  * "orchestrator/alpha"). This avoids the classic prefix-isolation bypass.
70
115
  */
71
- export declare function passesScopeFilter(oauthCtx: OAuthCtx, row: ScopeFilterable): boolean;
116
+ export declare function passesScopeFilter<T extends ScopeFilterable>(oauthCtx: OAuthCtx, row: T, grantFields?: GrantFieldDeclaration): boolean;
72
117
  /**
73
118
  * Filter a list of rows (post-query). Used by list_* tools.
119
+ *
120
+ * `grantFields` — see `passesScopeFilter`. Defaults to `[]` (0.4.0-identical
121
+ * behaviour).
74
122
  */
75
- export declare function scopeFilterList<T extends ScopeFilterable>(oauthCtx: OAuthCtx, rows: T[]): T[];
123
+ export declare function scopeFilterList<T extends ScopeFilterable>(oauthCtx: OAuthCtx, rows: T[], grantFields?: GrantFieldDeclaration): T[];
76
124
  /**
77
125
  * Assert a single row passes (get_* tools). Returns the row when allowed, null
78
126
  * otherwise — callers translate null to a 404-equivalent "not found" MCP error
79
127
  * to avoid leaking the difference between "absent" and "filtered out".
128
+ *
129
+ * `grantFields` — see `passesScopeFilter`. Defaults to `[]` (0.4.0-identical
130
+ * behaviour).
80
131
  */
81
- export declare function scopeFilterGet<T extends ScopeFilterable>(oauthCtx: OAuthCtx, row: T | null | undefined): T | null;
132
+ export declare function scopeFilterGet<T extends ScopeFilterable>(oauthCtx: OAuthCtx, row: T | null | undefined, grantFields?: GrantFieldDeclaration): T | null;
82
133
  /**
83
134
  * Helper for callers that want a single "is master / legacy" predicate.
84
135
  */
@@ -1 +1 @@
1
- {"version":3,"file":"scope-filter.d.ts","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAKjC,CAAC;AAWF;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAIpD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,eAAe,GACnB,OAAO,CAYT;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,eAAe,EACvD,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EAAE,GACR,CAAC,EAAE,CAGL;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,eAAe,EACtD,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACxB,CAAC,GAAG,IAAI,CAIV;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAGtD"}
1
+ {"version":3,"file":"scope-filter.d.ts","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAKjC,CAAC;AAWF;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAIpD;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,MAAM,EAAE,CAAC;AActD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,eAAe,EACzD,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,CAAC,EACN,WAAW,GAAE,qBAAuC,GACnD,OAAO,CAgBT;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,eAAe,EACvD,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EAAE,EACT,WAAW,GAAE,qBAAuC,GACnD,CAAC,EAAE,CAGL;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,eAAe,EACtD,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EACzB,WAAW,GAAE,qBAAuC,GACnD,CAAC,GAAG,IAAI,CAIV;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAGtD"}
@@ -5,6 +5,31 @@
5
5
  * implementation; the only change was replacing a framework-specific context
6
6
  * type with the framework-agnostic `OAuthCtx` from `./types`.
7
7
  *
8
+ * Contract (0.5.0 — additive, widening only, vs. 0.4.0):
9
+ * - Every exported predicate/filter now accepts an OPTIONAL `grantFields`
10
+ * parameter: `readonly string[]`, defaulting to `[]`. Each entry names a
11
+ * property on the row that the CALLER declares as a per-row grant (e.g.
12
+ * "agents", "pilot", "fulfilledBy"). The package never hardcodes which
13
+ * fields are grants for which table — that declaration is data supplied
14
+ * by the caller at the call site, not business knowledge baked into this
15
+ * shared module (no-hardcoded-business-knowledge).
16
+ * - A row passes for a non-master caller if createdBy/namespace already
17
+ * passed (0.4.0 behaviour, unchanged) OR if ANY declared grant field's
18
+ * value names an identity in `oauthCtx.fromAllowList` — the field may be
19
+ * a single string (e.g. "pilot") or a string[] (e.g. "agents"): a string
20
+ * value matches by equality, an array value matches if any element is in
21
+ * `fromAllowList`.
22
+ * - `grantFields` defaulting to `[]` means a caller that declares NO grants
23
+ * gets EXACTLY the 0.4.0 behaviour, byte-for-byte — this is a pure
24
+ * widening, no existing caller's behaviour changes without an explicit
25
+ * opt-in declaration at its own call site.
26
+ * - This closes the OTHER pole of the fail-closed defect: 0.3.0/0.4.0
27
+ * closed "a right granted by ABSENCE" (missing oauthCtx silently passing
28
+ * everything); this closes "a right REFUSED by absence" — a grant that
29
+ * IS present on the row (a named agent, pilot, or fulfiller) but that the
30
+ * filter was structurally blind to because it only ever consulted
31
+ * createdBy/namespace.
32
+ *
8
33
  * Contract (0.3.0 — BREAKING vs. 0.2.0):
9
34
  * - `oauthCtx` is now MANDATORY. A right is never granted by absence: the
10
35
  * 0.2.0 behaviour of `oauthCtx === undefined` silently passing everything
@@ -57,6 +82,15 @@ export function isMasterScope(ctx) {
57
82
  return true;
58
83
  return ctx.fromAllowList.includes("*");
59
84
  }
85
+ const NO_GRANT_FIELDS = [];
86
+ function grantFieldMatches(value, fromAllowList) {
87
+ if (typeof value === "string")
88
+ return fromAllowList.includes(value);
89
+ if (Array.isArray(value)) {
90
+ return value.some((entry) => typeof entry === "string" && fromAllowList.includes(entry));
91
+ }
92
+ return false;
93
+ }
60
94
  /**
61
95
  * Core predicate. Returns true when the row is visible to the caller.
62
96
  *
@@ -64,6 +98,9 @@ export function isMasterScope(ctx) {
64
98
  * - row.createdBy ∈ oauthCtx.fromAllowList → true
65
99
  * - row.namespace === prefix OR startsWith prefix+'/'
66
100
  * for any prefix ∈ oauthCtx.namespaceReadPrefixes → true
101
+ * - row[grantField] (for any declared grantField) names an identity in
102
+ * oauthCtx.fromAllowList, where the field is a string (equality) or a
103
+ * string[] (any-element membership) → true
67
104
  * - otherwise → false
68
105
  *
69
106
  * `oauthCtx` is mandatory (see module-level contract note above): an omitted
@@ -71,11 +108,16 @@ export function isMasterScope(ctx) {
71
108
  * need the old legacy-bearer wildcard behaviour must pass `LEGACY_WILDCARD_CTX`
72
109
  * explicitly.
73
110
  *
111
+ * `grantFields` (0.5.0, optional, defaults to `[]`) lets a caller widen the
112
+ * predicate for its own table without this package hardcoding which fields
113
+ * are grants — see the module-level 0.5.0 contract note above. Omitting it
114
+ * (or passing `[]`) reproduces 0.4.0 behaviour exactly.
115
+ *
74
116
  * Substring matches that don't fall on a '/' boundary are explicitly rejected
75
117
  * (e.g. namespace="orchestrator/alphabet" does NOT match prefix
76
118
  * "orchestrator/alpha"). This avoids the classic prefix-isolation bypass.
77
119
  */
78
- export function passesScopeFilter(oauthCtx, row) {
120
+ export function passesScopeFilter(oauthCtx, row, grantFields = NO_GRANT_FIELDS) {
79
121
  assertOauthCtx(oauthCtx);
80
122
  if (isMasterScope(oauthCtx))
81
123
  return true;
@@ -90,25 +132,36 @@ export function passesScopeFilter(oauthCtx, row) {
90
132
  return true;
91
133
  }
92
134
  }
135
+ for (const field of grantFields) {
136
+ const value = row[field];
137
+ if (grantFieldMatches(value, oauthCtx.fromAllowList))
138
+ return true;
139
+ }
93
140
  return false;
94
141
  }
95
142
  /**
96
143
  * Filter a list of rows (post-query). Used by list_* tools.
144
+ *
145
+ * `grantFields` — see `passesScopeFilter`. Defaults to `[]` (0.4.0-identical
146
+ * behaviour).
97
147
  */
98
- export function scopeFilterList(oauthCtx, rows) {
148
+ export function scopeFilterList(oauthCtx, rows, grantFields = NO_GRANT_FIELDS) {
99
149
  assertOauthCtx(oauthCtx);
100
- return rows.filter((r) => passesScopeFilter(oauthCtx, r));
150
+ return rows.filter((r) => passesScopeFilter(oauthCtx, r, grantFields));
101
151
  }
102
152
  /**
103
153
  * Assert a single row passes (get_* tools). Returns the row when allowed, null
104
154
  * otherwise — callers translate null to a 404-equivalent "not found" MCP error
105
155
  * to avoid leaking the difference between "absent" and "filtered out".
156
+ *
157
+ * `grantFields` — see `passesScopeFilter`. Defaults to `[]` (0.4.0-identical
158
+ * behaviour).
106
159
  */
107
- export function scopeFilterGet(oauthCtx, row) {
160
+ export function scopeFilterGet(oauthCtx, row, grantFields = NO_GRANT_FIELDS) {
108
161
  assertOauthCtx(oauthCtx);
109
162
  if (row == null)
110
163
  return null;
111
- return passesScopeFilter(oauthCtx, row) ? row : null;
164
+ return passesScopeFilter(oauthCtx, row, grantFields) ? row : null;
112
165
  }
113
166
  /**
114
167
  * Helper for callers that want a single "is master / legacy" predicate.
@@ -1 +1 @@
1
- {"version":3,"file":"scope-filter.js","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAa;IAC3C,KAAK,EAAE,QAAQ;IACf,aAAa,EAAE,CAAC,GAAG,CAAC;IACpB,qBAAqB,EAAE,EAAE;IACzB,sBAAsB,EAAE,EAAE;CAC3B,CAAC;AAEF,SAAS,cAAc,CAAC,QAAkB;IACxC,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,SAAS,CACjB,2EAA2E;YACzE,0EAA0E,CAC7E,CAAC;IACJ,CAAC;AACH,CAAC;AAWD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAa;IACzC,cAAc,CAAC,GAAG,CAAC,CAAC;IACpB,IAAI,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAkB,EAClB,GAAoB;IAEpB,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzB,IAAI,aAAa,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC;IACrC,IAAI,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,qBAAqB,EAAE,CAAC;YAC/C,IAAI,SAAS,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACjC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;QACjD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAkB,EAClB,IAAS;IAET,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAkB,EAClB,GAAyB;IAEzB,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzB,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,iBAAiB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,GAAa;IAC3C,cAAc,CAAC,GAAG,CAAC,CAAC;IACpB,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"scope-filter.js","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantageos/cloud-identity",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Framework-agnostic identity + scope-filter primitives for VantagePeers Cloud (multi-tenant MCP).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",