@vantageos/cloud-identity 0.8.0 → 0.9.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 +23 -2
- package/README.md +80 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/person-principal.d.ts +195 -0
- package/dist/person-principal.d.ts.map +1 -0
- package/dist/person-principal.js +217 -0
- package/dist/person-principal.js.map +1 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,30 @@
|
|
|
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
|
-
## [
|
|
6
|
+
## [0.9.0]
|
|
7
7
|
|
|
8
|
-
**Additive —
|
|
8
|
+
**Additive — the person principal (`./person-principal`).** The decisions a
|
|
9
|
+
server needs to serve a signed-in person in its own name, extracted from the
|
|
10
|
+
first consumer so that every other one wires them the same way.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `resolvePersonPrincipal`: a presented token's record -> `{ subject, orgSlug,
|
|
15
|
+
orgRole?, actor }`, or a typed refusal (not live, not a person's, no
|
|
16
|
+
organisation, organisation inactive or unmapped). Only the token's own
|
|
17
|
+
organisation is ever looked up.
|
|
18
|
+
- `resolvePersonActingName` / `checkPersonCallShape`: the own-name rule. The
|
|
19
|
+
acting identity comes from the principal; another user's name and an agent's
|
|
20
|
+
name without that agent's credential are refused.
|
|
21
|
+
- `resolvePersonTenantAccess`: strict organisation equality; unstamped refused.
|
|
22
|
+
- `resolveWriterRole` / `requireWriterRole`: writer-role assertion, fail closed.
|
|
23
|
+
- `PERSON_ACTOR_PREFIX`, `personActorName`, `isPersonActorName`,
|
|
24
|
+
`personTokenRecordSchema`, `PersonRefusalError` and the refusal types.
|
|
25
|
+
- New subpath export `./person-principal`.
|
|
26
|
+
|
|
27
|
+
## [0.8.0]
|
|
28
|
+
|
|
29
|
+
**Additive — authorize a PERSON and bind an organisation (0.8.0).**
|
|
9
30
|
A connector's `/authorize` that auto-approves with no user authentication, and
|
|
10
31
|
derives the code's user from a client-registration profile, makes every client
|
|
11
32
|
an anonymous app. This release is the flow that replaces it: Clerk session,
|
package/README.md
CHANGED
|
@@ -186,6 +186,15 @@ Visibility is granted two ways: the row's `createdBy` appears in the caller's
|
|
|
186
186
|
`namespaceReadPrefixes`. Prefixes match on a path boundary, so a caller allowed
|
|
187
187
|
`team/finance` does not thereby see `team/finance-archive`.
|
|
188
188
|
|
|
189
|
+
A third way is opt-in per call. All three functions take an optional last
|
|
190
|
+
argument, `grantFields` (type `GrantFieldDeclaration`, a list of property
|
|
191
|
+
names). It names the row properties that carry per-row grants, such as a
|
|
192
|
+
mission's `agents`. A row is then also visible when one of those properties
|
|
193
|
+
names an identity in the caller's `fromAllowList`. The property can hold a
|
|
194
|
+
single string or an array of strings; a value of any other shape is skipped,
|
|
195
|
+
not matched. The list is data supplied by your call site; the package
|
|
196
|
+
hardcodes no field names. Omitting it, or passing `[]`, adds nothing.
|
|
197
|
+
|
|
189
198
|
**Token validation**
|
|
190
199
|
|
|
191
200
|
- `validateMasterBearer(header, secret)` — compares an `Authorization: Bearer`
|
|
@@ -213,6 +222,8 @@ want to catch by type, and `BearerPayload`, the type returned by
|
|
|
213
222
|
- `ValidateMasterBearerResult` — the return type of `validateMasterBearer`.
|
|
214
223
|
- `ScopeFilterable` — the minimal row shape (`{ createdBy?, namespace? }`)
|
|
215
224
|
accepted by `passesScopeFilter` / `scopeFilterList` / `scopeFilterGet`.
|
|
225
|
+
- `GrantFieldDeclaration` — `readonly string[]`, the optional `grantFields`
|
|
226
|
+
argument of those three functions.
|
|
216
227
|
- `SessionIdentity` — the human-path shape `requireTenantId` accepts under
|
|
217
228
|
`{ kind: "session", identity }` (see "Both caller paths, one contract").
|
|
218
229
|
- `TenantSource` — the discriminated union `requireTenantId` accepts:
|
|
@@ -370,6 +381,75 @@ Limits, stated: `redirect_uri` matching is exact (no loopback port variation);
|
|
|
370
381
|
the package signs no access token and no id token and issues no refresh token (the consumer mints the token
|
|
371
382
|
from the claims); it fetches no key set and calls no Clerk API itself.
|
|
372
383
|
|
|
384
|
+
## Wiring person sign-in in an MCP server
|
|
385
|
+
|
|
386
|
+
A person signed in through an OAuth connector reaches your MCP server holding a
|
|
387
|
+
bearer you minted for them (see "Authorize a person and bind an organisation").
|
|
388
|
+
`@vantageos/cloud-identity/person-principal` turns that bearer's token record
|
|
389
|
+
into the person's principal and answers the three questions every write needs.
|
|
390
|
+
It is pure: you do the lookups, it decides. A server that serves people (a PDF
|
|
391
|
+
inspector, a registry, a CRM) wires it the same way:
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
import {
|
|
395
|
+
resolvePersonPrincipal,
|
|
396
|
+
resolvePersonActingName,
|
|
397
|
+
resolvePersonTenantAccess,
|
|
398
|
+
resolveWriterRole,
|
|
399
|
+
type PersonPrincipal,
|
|
400
|
+
type PersonRefusal,
|
|
401
|
+
} from "@vantageos/cloud-identity/person-principal";
|
|
402
|
+
|
|
403
|
+
// 1. Who is calling: the token row you looked up by the bearer's digest.
|
|
404
|
+
const who = await resolvePersonPrincipal(
|
|
405
|
+
{ principal: row.principal, subject: row.userId, orgSlug: row.orgSlug,
|
|
406
|
+
orgRole: row.orgRole, revokedAt: row.revokedAt, expiresAt: row.expiresAt },
|
|
407
|
+
{ now: Date.now(), lookupOrganisation: (slug) => orgTable.get(slug) },
|
|
408
|
+
"my-tool",
|
|
409
|
+
);
|
|
410
|
+
if (!who.ok) return deny(who.refusal);
|
|
411
|
+
|
|
412
|
+
// 2. Under what name: the token's, never the argument's.
|
|
413
|
+
const name = resolvePersonActingName({
|
|
414
|
+
principal: who.principal, claimedName: args.createdBy,
|
|
415
|
+
agentCredential: presentedAgent, door: "my-tool",
|
|
416
|
+
});
|
|
417
|
+
if (!name.ok) return deny(name.refusal);
|
|
418
|
+
|
|
419
|
+
// 3. Which rows, and may they write.
|
|
420
|
+
const inOrg = resolvePersonTenantAccess({
|
|
421
|
+
principal: who.principal, rowOrgId: record.orgId, door: "my-tool" });
|
|
422
|
+
const mayWrite = resolveWriterRole({
|
|
423
|
+
role: who.principal.orgRole, writerRoles: writerRolesOfOrg, door: "my-tool",
|
|
424
|
+
orgSlug: who.principal.orgSlug });
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
- `resolvePersonPrincipal(token, deps, door)` refuses a token that is expired,
|
|
428
|
+
revoked, not a person's, bound to no organisation, or bound to an inactive or
|
|
429
|
+
unmapped one. It looks up only the token's own organisation.
|
|
430
|
+
- `resolvePersonActingName` returns `{ ok: true, actingAs: "person", actor }`
|
|
431
|
+
for no name or the person's own (`user:<subject>`), and refuses another
|
|
432
|
+
user's name (`PERSON_ACTS_AS_ITSELF`) and an agent's name without that
|
|
433
|
+
agent's own credential (`AGENT_CREDENTIAL_REQUIRED`, or
|
|
434
|
+
`AGENT_IDENTITY_MISMATCH` when the credential belongs to another agent).
|
|
435
|
+
`checkPersonCallShape` is the same rule for a backend door that receives a
|
|
436
|
+
person's call already stripped of its name. `PERSON_ACTOR_PREFIX`,
|
|
437
|
+
`personActorName` and `isPersonActorName` spell and reserve the actor name:
|
|
438
|
+
refuse it when someone registers an agent.
|
|
439
|
+
- `resolvePersonTenantAccess` is a strict equality on the organisation; an
|
|
440
|
+
unstamped row is refused.
|
|
441
|
+
- `resolveWriterRole` / `requireWriterRole` check the verified role against a
|
|
442
|
+
writer allowlist you hold as data. A missing role, a missing list and an
|
|
443
|
+
empty list all refuse. `requireWriterRole` throws `PersonRefusalError`.
|
|
444
|
+
- Every refusal is a `PersonRefusal` (`PersonRefusalCode`, `PersonRefusalReason`,
|
|
445
|
+
`door`, `detail`); you choose the transport shape. `personTokenRecordSchema`,
|
|
446
|
+
`PersonTokenRecord`, `PersonPrincipal`, `OrganisationState`,
|
|
447
|
+
`PersonPrincipalDeps`, `PersonPrincipalResult`, `PersonActingNameResult` and
|
|
448
|
+
`PersonAccessResult` are the input and result types.
|
|
449
|
+
|
|
450
|
+
The module verifies no credential. The token record must come from your own
|
|
451
|
+
lookup of the bearer the person presented, never from a request body.
|
|
452
|
+
|
|
373
453
|
## What this package does not do
|
|
374
454
|
|
|
375
455
|
Worth reading before you rely on it.
|
package/dist/index.d.ts
CHANGED
|
@@ -27,4 +27,6 @@ export { buildDiscoveryDocument, buildUserInfo } from "./oidc.js";
|
|
|
27
27
|
export type { DiscoveryConfig, DiscoveryDocument, DiscoveryResult, ClerkUserLike, UserInfo, UserInfoResult, } from "./oidc.js";
|
|
28
28
|
export { oauthErrorFor, pkceChallengeFromVerifier } from "./authorize-shared.js";
|
|
29
29
|
export type { AuthorizeRefusal, AuthorizeRefusalReason, } from "./authorize-shared.js";
|
|
30
|
+
export { PERSON_ACTOR_PREFIX, PersonRefusalError, personActorName, isPersonActorName, personTokenRecordSchema, resolvePersonPrincipal, resolvePersonActingName, checkPersonCallShape, resolvePersonTenantAccess, resolveWriterRole, requireWriterRole, } from "./person-principal.js";
|
|
31
|
+
export type { PersonRefusal, PersonRefusalCode, PersonRefusalReason, PersonTokenRecord, PersonPrincipal, OrganisationState, PersonPrincipalDeps, PersonPrincipalResult, PersonActingNameResult, PersonAccessResult, } from "./person-principal.js";
|
|
30
32
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,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;AAKzB,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,eAAe,EACf,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,6BAA6B,EAC7B,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,YAAY,EACV,qBAAqB,EACrB,aAAa,EACb,YAAY,EACZ,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,YAAY,EACV,QAAQ,EACR,SAAS,EACT,0BAA0B,EAC1B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAClE,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACjF,YAAY,EACV,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -25,4 +25,8 @@ export { exchangeAuthorizationCode } from "./token-exchange.js";
|
|
|
25
25
|
export { verifyClerkSessionToken } from "./clerk-session.js";
|
|
26
26
|
export { buildDiscoveryDocument, buildUserInfo } from "./oidc.js";
|
|
27
27
|
export { oauthErrorFor, pkceChallengeFromVerifier } from "./authorize-shared.js";
|
|
28
|
+
// 0.9.0 — The person principal: a verified sign-in (person + organisation) as
|
|
29
|
+
// pure data, the own-name actor rule, the tenant compare and the writer-role
|
|
30
|
+
// assertion. Every consumer wires a person the same way.
|
|
31
|
+
export { PERSON_ACTOR_PREFIX, PersonRefusalError, personActorName, isPersonActorName, personTokenRecordSchema, resolvePersonPrincipal, resolvePersonActingName, checkPersonCallShape, resolvePersonTenantAccess, resolveWriterRole, requireWriterRole, } from "./person-principal.js";
|
|
28
32
|
//# 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,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,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;AAWzB,sEAAsE;AACtE,yEAAyE;AACzE,mEAAmE;AACnE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAY/B,+EAA+E;AAC/E,mEAAmE;AACnE,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAa7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAOhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAQ7D,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AASlE,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,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;AAWzB,sEAAsE;AACtE,yEAAyE;AACzE,mEAAmE;AACnE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAY/B,+EAA+E;AAC/E,mEAAmE;AACnE,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAa7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAOhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAQ7D,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AASlE,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAMjF,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vantageos/cloud-identity 0.9.0 — the person principal.
|
|
3
|
+
*
|
|
4
|
+
* A PERSON signed in through an OAuth connector (Claude, ChatGPT, any MCP
|
|
5
|
+
* client) is not an agent. This module is the single, framework-agnostic
|
|
6
|
+
* answer to four questions a consumer must not re-implement:
|
|
7
|
+
*
|
|
8
|
+
* 1. Who is the person, and in which organisation?
|
|
9
|
+
* `resolvePersonPrincipal` — from the record of an access token that the
|
|
10
|
+
* consumer already looked up by its digest. Never from an argument.
|
|
11
|
+
* 2. Under what name does the person act?
|
|
12
|
+
* `resolvePersonActingName` / `checkPersonCallShape` — its OWN name,
|
|
13
|
+
* "user:<subject>", taken from the principal. An agent name needs that
|
|
14
|
+
* agent's own verified credential; another user's name is refused.
|
|
15
|
+
* 3. Is a row in the person's organisation?
|
|
16
|
+
* `resolvePersonTenantAccess` — a strict equality on the organisation.
|
|
17
|
+
* 4. May the person write?
|
|
18
|
+
* `resolveWriterRole` / `requireWriterRole` — the verified role must be on
|
|
19
|
+
* the writer allowlist the consumer holds as data. Fail closed.
|
|
20
|
+
*
|
|
21
|
+
* Pure functions over plain inputs: no Clerk SDK, no database, no network, no
|
|
22
|
+
* master or bypass flag anywhere in the input. The consumer performs the
|
|
23
|
+
* lookups (the token row, the organisation row, the writer list) and passes the
|
|
24
|
+
* results in; this module only decides. Every refusal is a typed value carrying
|
|
25
|
+
* its own code, reason and door, so a caller can branch on content, and an
|
|
26
|
+
* absence (no organisation, no role, no list) is a refusal, never a default.
|
|
27
|
+
*
|
|
28
|
+
* @security This module verifies NOTHING about the credential itself. The
|
|
29
|
+
* token record must come from a lookup of the bearer the person presented, made
|
|
30
|
+
* by the consumer's trusted transport layer. Passing a client-supplied object
|
|
31
|
+
* makes that object the source of subject, organisation and role.
|
|
32
|
+
*/
|
|
33
|
+
import { z } from "zod";
|
|
34
|
+
export type PersonRefusalCode = "RBAC_DENIED" | "PERSON_ACTS_AS_ITSELF" | "AGENT_CREDENTIAL_REQUIRED" | "AGENT_IDENTITY_MISMATCH";
|
|
35
|
+
export type PersonRefusalReason = "person-token-not-live" | "not-a-person-token" | "person-token-no-org" | "org-not-active" | "person-acts-as-itself" | "agent-proof-on-person-path" | "agent-credential-required" | "agent-identity-mismatch" | "other-organisation" | "role-not-writer";
|
|
36
|
+
/** The typed refusal. A non-empty object carrying its own code and door. */
|
|
37
|
+
export type PersonRefusal = {
|
|
38
|
+
code: PersonRefusalCode;
|
|
39
|
+
reason: PersonRefusalReason;
|
|
40
|
+
/** The consumer's own name for the entry point that refused. */
|
|
41
|
+
door: string;
|
|
42
|
+
/** One sentence, safe to show to the caller. Never a credential. */
|
|
43
|
+
detail: string;
|
|
44
|
+
/** The acting name the caller typed, when the refusal is about a name. */
|
|
45
|
+
claimed?: string;
|
|
46
|
+
/** The person's own actor name, when the refusal is about a name. */
|
|
47
|
+
self?: string;
|
|
48
|
+
/** The verified role, when the refusal is about a role (null: none). */
|
|
49
|
+
role?: string | null;
|
|
50
|
+
/** The organisation the decision was taken in, when it applies. */
|
|
51
|
+
orgSlug?: string | null;
|
|
52
|
+
};
|
|
53
|
+
/** Thrown by the throwing forms. Carries the typed refusal untouched. */
|
|
54
|
+
export declare class PersonRefusalError extends Error {
|
|
55
|
+
readonly refusal: PersonRefusal;
|
|
56
|
+
constructor(refusal: PersonRefusal);
|
|
57
|
+
}
|
|
58
|
+
/** How a person is recorded as "who did it": "user:<verified subject>". */
|
|
59
|
+
export declare const PERSON_ACTOR_PREFIX = "user:";
|
|
60
|
+
/** The actor name of a verified subject. */
|
|
61
|
+
export declare function personActorName(subject: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* True when `name` is spelled like a person's actor name. Such a name is
|
|
64
|
+
* RESERVED: a consumer refuses it at agent registration, so a stored actor
|
|
65
|
+
* "user:..." can only have been written from a verified subject.
|
|
66
|
+
*/
|
|
67
|
+
export declare function isPersonActorName(name: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* The record of a presented access token, in the package's vocabulary. The
|
|
70
|
+
* consumer maps its own row onto this shape (`subject` is the verified Clerk
|
|
71
|
+
* user id, `orgSlug` the organisation the token is bound to).
|
|
72
|
+
*/
|
|
73
|
+
export declare const personTokenRecordSchema: z.ZodObject<{
|
|
74
|
+
principal: z.ZodOptional<z.ZodString>;
|
|
75
|
+
subject: z.ZodString;
|
|
76
|
+
orgSlug: z.ZodOptional<z.ZodString>;
|
|
77
|
+
orgRole: z.ZodOptional<z.ZodString>;
|
|
78
|
+
revokedAt: z.ZodOptional<z.ZodNumber>;
|
|
79
|
+
expiresAt: z.ZodNumber;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
export type PersonTokenRecord = z.input<typeof personTokenRecordSchema>;
|
|
82
|
+
/** A verified person: who, in which organisation, with which role. */
|
|
83
|
+
export type PersonPrincipal = {
|
|
84
|
+
subject: string;
|
|
85
|
+
orgSlug: string;
|
|
86
|
+
/** Present only when the token carries a verified role. Never defaulted. */
|
|
87
|
+
orgRole?: string;
|
|
88
|
+
/** "user:<subject>" — the name this person acts under. */
|
|
89
|
+
actor: string;
|
|
90
|
+
};
|
|
91
|
+
/** The state of an organisation as the consumer's own mapping holds it. */
|
|
92
|
+
export type OrganisationState = {
|
|
93
|
+
isActive: boolean;
|
|
94
|
+
};
|
|
95
|
+
export type PersonPrincipalDeps<O extends OrganisationState> = {
|
|
96
|
+
/** Epoch milliseconds, supplied by the caller so the decision is pure. */
|
|
97
|
+
now: number;
|
|
98
|
+
/** Resolves the organisation row for a slug; null when unmapped. */
|
|
99
|
+
lookupOrganisation: (orgSlug: string) => Promise<O | null>;
|
|
100
|
+
};
|
|
101
|
+
export type PersonPrincipalResult<O extends OrganisationState> = {
|
|
102
|
+
ok: true;
|
|
103
|
+
principal: PersonPrincipal;
|
|
104
|
+
organisation: O;
|
|
105
|
+
} | {
|
|
106
|
+
ok: false;
|
|
107
|
+
refusal: PersonRefusal;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Resolves the person behind a presented token, or refuses. Checks, in order:
|
|
111
|
+
* the token is live (present, unrevoked, unexpired); it acts for a PERSON;
|
|
112
|
+
* it is bound to an organisation; that organisation exists and is active.
|
|
113
|
+
* The only organisation ever looked up is the token's own.
|
|
114
|
+
*/
|
|
115
|
+
export declare function resolvePersonPrincipal<O extends OrganisationState>(token: PersonTokenRecord | null | undefined, deps: PersonPrincipalDeps<O>, door: string): Promise<PersonPrincipalResult<O>>;
|
|
116
|
+
export type PersonActingNameResult = {
|
|
117
|
+
ok: true;
|
|
118
|
+
actingAs: "person";
|
|
119
|
+
actor: string;
|
|
120
|
+
} | {
|
|
121
|
+
ok: true;
|
|
122
|
+
actingAs: "agent";
|
|
123
|
+
actor: string;
|
|
124
|
+
} | {
|
|
125
|
+
ok: false;
|
|
126
|
+
refusal: PersonRefusal;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Decides under which name a person acts. The acting identity comes from the
|
|
130
|
+
* principal, never from the typed name:
|
|
131
|
+
*
|
|
132
|
+
* - no name, or the person's own name -> acts as itself;
|
|
133
|
+
* - another user's name -> PERSON_ACTS_AS_ITSELF;
|
|
134
|
+
* - an agent's name -> only with that agent's own verified
|
|
135
|
+
* credential (`agentCredential`,
|
|
136
|
+
* resolved by the consumer): none ->
|
|
137
|
+
* AGENT_CREDENTIAL_REQUIRED, a
|
|
138
|
+
* different agent -> AGENT_IDENTITY_MISMATCH.
|
|
139
|
+
*/
|
|
140
|
+
export declare function resolvePersonActingName(input: {
|
|
141
|
+
/** Only the actor name is read: the rule needs no organisation. */
|
|
142
|
+
principal: Pick<PersonPrincipal, "actor">;
|
|
143
|
+
claimedName?: string | null;
|
|
144
|
+
/** The agent the presented credential resolved to, when one was presented. */
|
|
145
|
+
agentCredential?: {
|
|
146
|
+
agentName: string;
|
|
147
|
+
} | null;
|
|
148
|
+
door: string;
|
|
149
|
+
}): PersonActingNameResult;
|
|
150
|
+
/**
|
|
151
|
+
* The shape of a call that reaches a backend door ON a person's behalf: it
|
|
152
|
+
* carries no acting name (the transport already stripped the person's own) and
|
|
153
|
+
* no agent proof (a person that presented an agent credential acts as that
|
|
154
|
+
* agent, on the agent path). Returns the refusal, or null when the shape is
|
|
155
|
+
* right.
|
|
156
|
+
*/
|
|
157
|
+
export declare function checkPersonCallShape(input: {
|
|
158
|
+
door: string;
|
|
159
|
+
actingName?: string;
|
|
160
|
+
agentProof?: boolean;
|
|
161
|
+
}): PersonRefusal | null;
|
|
162
|
+
export type PersonAccessResult = {
|
|
163
|
+
ok: true;
|
|
164
|
+
} | {
|
|
165
|
+
ok: false;
|
|
166
|
+
refusal: PersonRefusal;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* A row is reachable only when its organisation stamp EQUALS the person's
|
|
170
|
+
* organisation. An absent or empty stamp never equals a resolved organisation,
|
|
171
|
+
* so an unstamped row is refused.
|
|
172
|
+
*/
|
|
173
|
+
export declare function resolvePersonTenantAccess(input: {
|
|
174
|
+
principal: PersonPrincipal;
|
|
175
|
+
rowOrgId: string | null | undefined;
|
|
176
|
+
door: string;
|
|
177
|
+
}): PersonAccessResult;
|
|
178
|
+
type WriterRoleInput = {
|
|
179
|
+
/** The VERIFIED role claim of the token. Absent or null is refused. */
|
|
180
|
+
role: string | null | undefined;
|
|
181
|
+
/** The writer allowlist the consumer holds as data. Empty refuses all. */
|
|
182
|
+
writerRoles: readonly string[];
|
|
183
|
+
door: string;
|
|
184
|
+
orgSlug?: string | null;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Non-throwing writer-role assertion. The role must appear EXACTLY in the
|
|
188
|
+
* allowlist; no hierarchy, no inheritance. An absent role, an absent list and
|
|
189
|
+
* an empty list all refuse: a missing list never means "all roles".
|
|
190
|
+
*/
|
|
191
|
+
export declare function resolveWriterRole(input: WriterRoleInput): PersonAccessResult;
|
|
192
|
+
/** Throwing form of {@link resolveWriterRole}; throws `PersonRefusalError`. */
|
|
193
|
+
export declare function requireWriterRole(input: WriterRoleInput): void;
|
|
194
|
+
export {};
|
|
195
|
+
//# sourceMappingURL=person-principal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"person-principal.d.ts","sourceRoot":"","sources":["../src/person-principal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vantageos/cloud-identity 0.9.0 — the person principal.
|
|
3
|
+
*
|
|
4
|
+
* A PERSON signed in through an OAuth connector (Claude, ChatGPT, any MCP
|
|
5
|
+
* client) is not an agent. This module is the single, framework-agnostic
|
|
6
|
+
* answer to four questions a consumer must not re-implement:
|
|
7
|
+
*
|
|
8
|
+
* 1. Who is the person, and in which organisation?
|
|
9
|
+
* `resolvePersonPrincipal` — from the record of an access token that the
|
|
10
|
+
* consumer already looked up by its digest. Never from an argument.
|
|
11
|
+
* 2. Under what name does the person act?
|
|
12
|
+
* `resolvePersonActingName` / `checkPersonCallShape` — its OWN name,
|
|
13
|
+
* "user:<subject>", taken from the principal. An agent name needs that
|
|
14
|
+
* agent's own verified credential; another user's name is refused.
|
|
15
|
+
* 3. Is a row in the person's organisation?
|
|
16
|
+
* `resolvePersonTenantAccess` — a strict equality on the organisation.
|
|
17
|
+
* 4. May the person write?
|
|
18
|
+
* `resolveWriterRole` / `requireWriterRole` — the verified role must be on
|
|
19
|
+
* the writer allowlist the consumer holds as data. Fail closed.
|
|
20
|
+
*
|
|
21
|
+
* Pure functions over plain inputs: no Clerk SDK, no database, no network, no
|
|
22
|
+
* master or bypass flag anywhere in the input. The consumer performs the
|
|
23
|
+
* lookups (the token row, the organisation row, the writer list) and passes the
|
|
24
|
+
* results in; this module only decides. Every refusal is a typed value carrying
|
|
25
|
+
* its own code, reason and door, so a caller can branch on content, and an
|
|
26
|
+
* absence (no organisation, no role, no list) is a refusal, never a default.
|
|
27
|
+
*
|
|
28
|
+
* @security This module verifies NOTHING about the credential itself. The
|
|
29
|
+
* token record must come from a lookup of the bearer the person presented, made
|
|
30
|
+
* by the consumer's trusted transport layer. Passing a client-supplied object
|
|
31
|
+
* makes that object the source of subject, organisation and role.
|
|
32
|
+
*/
|
|
33
|
+
import { z } from "zod";
|
|
34
|
+
/** Thrown by the throwing forms. Carries the typed refusal untouched. */
|
|
35
|
+
export class PersonRefusalError extends Error {
|
|
36
|
+
refusal;
|
|
37
|
+
constructor(refusal) {
|
|
38
|
+
super(`${refusal.code}: ${refusal.detail} (${refusal.reason}, ${refusal.door})`);
|
|
39
|
+
this.name = "PersonRefusalError";
|
|
40
|
+
this.refusal = refusal;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function refuse(code, reason, door, detail, extra = {}) {
|
|
44
|
+
return { code, reason, door, detail, ...extra };
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Actor name
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/** How a person is recorded as "who did it": "user:<verified subject>". */
|
|
50
|
+
export const PERSON_ACTOR_PREFIX = "user:";
|
|
51
|
+
/** NFC, lowercase, trim: the comparison form of an acting name. */
|
|
52
|
+
function normalizeName(name) {
|
|
53
|
+
return name.normalize("NFC").toLowerCase().trim();
|
|
54
|
+
}
|
|
55
|
+
/** The actor name of a verified subject. */
|
|
56
|
+
export function personActorName(subject) {
|
|
57
|
+
return `${PERSON_ACTOR_PREFIX}${subject}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* True when `name` is spelled like a person's actor name. Such a name is
|
|
61
|
+
* RESERVED: a consumer refuses it at agent registration, so a stored actor
|
|
62
|
+
* "user:..." can only have been written from a verified subject.
|
|
63
|
+
*/
|
|
64
|
+
export function isPersonActorName(name) {
|
|
65
|
+
return normalizeName(name).startsWith(PERSON_ACTOR_PREFIX);
|
|
66
|
+
}
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// 1. The principal
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
/**
|
|
71
|
+
* The record of a presented access token, in the package's vocabulary. The
|
|
72
|
+
* consumer maps its own row onto this shape (`subject` is the verified Clerk
|
|
73
|
+
* user id, `orgSlug` the organisation the token is bound to).
|
|
74
|
+
*/
|
|
75
|
+
export const personTokenRecordSchema = z.object({
|
|
76
|
+
principal: z.string().optional(),
|
|
77
|
+
subject: z.string(),
|
|
78
|
+
orgSlug: z.string().optional(),
|
|
79
|
+
orgRole: z.string().optional(),
|
|
80
|
+
revokedAt: z.number().optional(),
|
|
81
|
+
expiresAt: z.number(),
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Resolves the person behind a presented token, or refuses. Checks, in order:
|
|
85
|
+
* the token is live (present, unrevoked, unexpired); it acts for a PERSON;
|
|
86
|
+
* it is bound to an organisation; that organisation exists and is active.
|
|
87
|
+
* The only organisation ever looked up is the token's own.
|
|
88
|
+
*/
|
|
89
|
+
export async function resolvePersonPrincipal(token, deps, door) {
|
|
90
|
+
const deny = (reason, detail) => ({
|
|
91
|
+
ok: false,
|
|
92
|
+
refusal: refuse("RBAC_DENIED", reason, door, detail),
|
|
93
|
+
});
|
|
94
|
+
const parsed = personTokenRecordSchema.safeParse(token);
|
|
95
|
+
if (!parsed.success ||
|
|
96
|
+
parsed.data.revokedAt !== undefined ||
|
|
97
|
+
parsed.data.expiresAt < deps.now) {
|
|
98
|
+
return deny("person-token-not-live", "verifiedPerson names no live access token");
|
|
99
|
+
}
|
|
100
|
+
const row = parsed.data;
|
|
101
|
+
if (row.principal !== "person" || row.subject === "") {
|
|
102
|
+
return deny("not-a-person-token", "verifiedPerson names a token that does not act for a person");
|
|
103
|
+
}
|
|
104
|
+
const orgSlug = row.orgSlug;
|
|
105
|
+
if (orgSlug === undefined || orgSlug === "") {
|
|
106
|
+
return deny("person-token-no-org", "the person's token is bound to no organisation");
|
|
107
|
+
}
|
|
108
|
+
const organisation = await deps.lookupOrganisation(orgSlug);
|
|
109
|
+
if (organisation === null || !organisation.isActive) {
|
|
110
|
+
return deny("org-not-active", `Org "${orgSlug}" not in client_org_mapping or inactive`);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
ok: true,
|
|
114
|
+
organisation,
|
|
115
|
+
principal: {
|
|
116
|
+
subject: row.subject,
|
|
117
|
+
orgSlug,
|
|
118
|
+
...(row.orgRole !== undefined ? { orgRole: row.orgRole } : {}),
|
|
119
|
+
actor: personActorName(row.subject),
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Decides under which name a person acts. The acting identity comes from the
|
|
125
|
+
* principal, never from the typed name:
|
|
126
|
+
*
|
|
127
|
+
* - no name, or the person's own name -> acts as itself;
|
|
128
|
+
* - another user's name -> PERSON_ACTS_AS_ITSELF;
|
|
129
|
+
* - an agent's name -> only with that agent's own verified
|
|
130
|
+
* credential (`agentCredential`,
|
|
131
|
+
* resolved by the consumer): none ->
|
|
132
|
+
* AGENT_CREDENTIAL_REQUIRED, a
|
|
133
|
+
* different agent -> AGENT_IDENTITY_MISMATCH.
|
|
134
|
+
*/
|
|
135
|
+
export function resolvePersonActingName(input) {
|
|
136
|
+
const { principal, door } = input;
|
|
137
|
+
const claimed = input.claimedName;
|
|
138
|
+
if (claimed === undefined || claimed === null) {
|
|
139
|
+
return { ok: true, actingAs: "person", actor: principal.actor };
|
|
140
|
+
}
|
|
141
|
+
const name = normalizeName(claimed);
|
|
142
|
+
if (name === normalizeName(principal.actor)) {
|
|
143
|
+
return { ok: true, actingAs: "person", actor: principal.actor };
|
|
144
|
+
}
|
|
145
|
+
if (name.startsWith(PERSON_ACTOR_PREFIX)) {
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
refusal: refuse("PERSON_ACTS_AS_ITSELF", "person-acts-as-itself", door, `a person acts only in its own name; this call names "${claimed}"`, { claimed, self: principal.actor }),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const credential = input.agentCredential;
|
|
152
|
+
if (credential === undefined || credential === null) {
|
|
153
|
+
return {
|
|
154
|
+
ok: false,
|
|
155
|
+
refusal: refuse("AGENT_CREDENTIAL_REQUIRED", "agent-credential-required", door, `this call names "${claimed}" but carried no credential of that agent`, { claimed, self: principal.actor }),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (normalizeName(credential.agentName) !== name) {
|
|
159
|
+
return {
|
|
160
|
+
ok: false,
|
|
161
|
+
refusal: refuse("AGENT_IDENTITY_MISMATCH", "agent-identity-mismatch", door, `this call names "${claimed}" but the presented credential belongs to another agent`, { claimed, self: principal.actor }),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { ok: true, actingAs: "agent", actor: credential.agentName };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The shape of a call that reaches a backend door ON a person's behalf: it
|
|
168
|
+
* carries no acting name (the transport already stripped the person's own) and
|
|
169
|
+
* no agent proof (a person that presented an agent credential acts as that
|
|
170
|
+
* agent, on the agent path). Returns the refusal, or null when the shape is
|
|
171
|
+
* right.
|
|
172
|
+
*/
|
|
173
|
+
export function checkPersonCallShape(input) {
|
|
174
|
+
if (input.agentProof === true) {
|
|
175
|
+
return refuse("RBAC_DENIED", "agent-proof-on-person-path", input.door, "a person's call carries no agent credential and no verified actor");
|
|
176
|
+
}
|
|
177
|
+
if (input.actingName !== undefined) {
|
|
178
|
+
return refuse("PERSON_ACTS_AS_ITSELF", "person-acts-as-itself", input.door, `a person acts only in its own name; this call also names "${input.actingName}"`, { claimed: input.actingName });
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* A row is reachable only when its organisation stamp EQUALS the person's
|
|
184
|
+
* organisation. An absent or empty stamp never equals a resolved organisation,
|
|
185
|
+
* so an unstamped row is refused.
|
|
186
|
+
*/
|
|
187
|
+
export function resolvePersonTenantAccess(input) {
|
|
188
|
+
const { principal, rowOrgId, door } = input;
|
|
189
|
+
if (typeof rowOrgId === "string" && rowOrgId !== "" && rowOrgId === principal.orgSlug) {
|
|
190
|
+
return { ok: true };
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
ok: false,
|
|
194
|
+
refusal: refuse("RBAC_DENIED", "other-organisation", door, "the row is outside the person's organisation (tenant boundary)", { orgSlug: principal.orgSlug }),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Non-throwing writer-role assertion. The role must appear EXACTLY in the
|
|
199
|
+
* allowlist; no hierarchy, no inheritance. An absent role, an absent list and
|
|
200
|
+
* an empty list all refuse: a missing list never means "all roles".
|
|
201
|
+
*/
|
|
202
|
+
export function resolveWriterRole(input) {
|
|
203
|
+
const role = input.role ?? null;
|
|
204
|
+
if (role !== null && input.writerRoles.includes(role))
|
|
205
|
+
return { ok: true };
|
|
206
|
+
return {
|
|
207
|
+
ok: false,
|
|
208
|
+
refusal: refuse("RBAC_DENIED", "role-not-writer", input.door, "member role is not a writer role", { role, orgSlug: input.orgSlug ?? null }),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/** Throwing form of {@link resolveWriterRole}; throws `PersonRefusalError`. */
|
|
212
|
+
export function requireWriterRole(input) {
|
|
213
|
+
const r = resolveWriterRole(input);
|
|
214
|
+
if (!r.ok)
|
|
215
|
+
throw new PersonRefusalError(r.refusal);
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=person-principal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"person-principal.js","sourceRoot":"","sources":["../src/person-principal.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.
|
|
3
|
+
"version": "0.9.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",
|
|
@@ -53,6 +53,10 @@
|
|
|
53
53
|
"./oidc": {
|
|
54
54
|
"types": "./dist/oidc.d.ts",
|
|
55
55
|
"import": "./dist/oidc.js"
|
|
56
|
+
},
|
|
57
|
+
"./person-principal": {
|
|
58
|
+
"types": "./dist/person-principal.d.ts",
|
|
59
|
+
"import": "./dist/person-principal.js"
|
|
56
60
|
}
|
|
57
61
|
},
|
|
58
62
|
"files": [
|