@vantageos/cloud-identity 0.1.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 +18 -0
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/bearer-validation.d.ts +33 -0
- package/dist/bearer-validation.d.ts.map +1 -0
- package/dist/bearer-validation.js +61 -0
- package/dist/bearer-validation.js.map +1 -0
- package/dist/crypto.d.ts +20 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +34 -0
- package/dist/crypto.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/scope-filter.d.ts +71 -0
- package/dist/scope-filter.d.ts.map +1 -0
- package/dist/scope-filter.js +95 -0
- package/dist/scope-filter.js.map +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@vantageos/cloud-identity` are documented here.
|
|
4
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] - 2026-06-04
|
|
7
|
+
|
|
8
|
+
Initial release.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `crypto.timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean` — constant-time byte comparison with branch-timing-leak mitigation on length mismatch.
|
|
12
|
+
- `scope-filter.passesScopeFilter / scopeFilterList / scopeFilterGet` — row-level visibility filter honouring `fromAllowList` + `namespaceReadPrefixes` with master pass-through and legacy-bearer back-compat.
|
|
13
|
+
- `bearer-validation.validateMasterBearer` — `Authorization: Bearer <token>` parsing + sha256 constant-time match against a master secret.
|
|
14
|
+
- Public type surface: `OAuthCtx`, `ScopeProfile`, `NamespacePrefix`, `FromAllowListEntry`, `ValidateMasterBearerResult`.
|
|
15
|
+
|
|
16
|
+
### Notes
|
|
17
|
+
- `getEffectiveWorkspaceId` deferred to 0.2.0 (depends on `vantageos-crm` workspace helper, owned by Theta).
|
|
18
|
+
- No `provenance: true` in `publishConfig` — provenance only from CI per fleet friction lesson `npm-publish-provenance-only-from-ci-not-local-host`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VantagePeers Cloud
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# @vantageos/cloud-identity
|
|
2
|
+
|
|
3
|
+
Framework-agnostic identity + scope-filter primitives for **VantagePeers Cloud** (multi-tenant MCP).
|
|
4
|
+
|
|
5
|
+
Consumed by `vantage-peers-mcp` and `@vantageos/vantage-crm-mcp` — provides the small, testable building blocks that gate cross-tenant data access in the MCP transport layer.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @vantageos/cloud-identity
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Modules
|
|
14
|
+
|
|
15
|
+
| Module | Symbols | Purpose |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `@vantageos/cloud-identity/crypto` | `timingSafeEqual` | Constant-time byte comparison (no branch-timing leak). |
|
|
18
|
+
| `@vantageos/cloud-identity/scope-filter` | `passesScopeFilter`, `scopeFilterList`, `scopeFilterGet` | Row-level visibility filter on `(createdBy, namespace)` using the caller's OAuth scope. |
|
|
19
|
+
| `@vantageos/cloud-identity/bearer-validation` | `validateMasterBearer` | Bearer-token parsing + sha256 constant-time match against a configured master secret. |
|
|
20
|
+
| `@vantageos/cloud-identity/types` | `OAuthCtx`, `ScopeProfile`, `NamespacePrefix`, `FromAllowListEntry`, `ValidateMasterBearerResult` | Public type surface. |
|
|
21
|
+
|
|
22
|
+
## 0.1.0 scope
|
|
23
|
+
|
|
24
|
+
This release ships the **transport-agnostic** core:
|
|
25
|
+
|
|
26
|
+
- `crypto.timingSafeEqual`
|
|
27
|
+
- `scope-filter.{passesScopeFilter, scopeFilterList, scopeFilterGet}`
|
|
28
|
+
- `bearer-validation.validateMasterBearer`
|
|
29
|
+
- types
|
|
30
|
+
|
|
31
|
+
## 0.2.0 roadmap
|
|
32
|
+
|
|
33
|
+
- `getEffectiveWorkspaceId` (workspace resolution helper, sourced from `vantageos-crm`).
|
|
34
|
+
- D1 ghost Clerk identity injection.
|
|
35
|
+
- Co-designed in a Theta cycle.
|
|
36
|
+
|
|
37
|
+
## Usage examples
|
|
38
|
+
|
|
39
|
+
### Constant-time byte comparison
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { timingSafeEqual } from "@vantageos/cloud-identity/crypto";
|
|
43
|
+
|
|
44
|
+
const a = new TextEncoder().encode(presentedHash);
|
|
45
|
+
const b = new TextEncoder().encode(expectedHash);
|
|
46
|
+
if (!(await timingSafeEqual(a, b))) {
|
|
47
|
+
return new Response("forbidden", { status: 403 });
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Scope-aware row filtering
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { scopeFilterList } from "@vantageos/cloud-identity/scope-filter";
|
|
55
|
+
import type { OAuthCtx } from "@vantageos/cloud-identity/types";
|
|
56
|
+
|
|
57
|
+
const ctx: OAuthCtx = {
|
|
58
|
+
fromAllowList: ["alice"],
|
|
59
|
+
namespaceReadPrefixes: ["orchestrator/alpha"],
|
|
60
|
+
namespaceWritePrefixes: [],
|
|
61
|
+
};
|
|
62
|
+
const visible = scopeFilterList(ctx, rowsFromConvex);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Master Bearer validation
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { validateMasterBearer } from "@vantageos/cloud-identity/bearer-validation";
|
|
69
|
+
|
|
70
|
+
const res = await validateMasterBearer(
|
|
71
|
+
request.headers.get("authorization") ?? undefined,
|
|
72
|
+
process.env.BEARER_SECRET_MASTER ?? "",
|
|
73
|
+
);
|
|
74
|
+
if (!res.ok) {
|
|
75
|
+
return new Response(`unauthorized: ${res.error}`, { status: 401 });
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Security doctrine
|
|
80
|
+
|
|
81
|
+
Canonical reference: `docs/cloud/security-multi-tenant.md` in the `vantage-peers` repo.
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
MIT — VantagePeers Cloud, 2026.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Master-token Bearer validation.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the master-gate logic in
|
|
5
|
+
* `vantage-memory/mcp-server/server-http.ts` (`masterOnlyMiddleware`) and
|
|
6
|
+
* HARDENED per @vantageos/cloud-identity 0.1.0 contract:
|
|
7
|
+
* - SHA-256 hash both the presented token AND the configured master secret,
|
|
8
|
+
* then compare the two digests with `timingSafeEqual` (constant time).
|
|
9
|
+
* - The original middleware did a direct `token !== masterToken` string
|
|
10
|
+
* compare which is non-constant-time. This brick fixes that leak so any
|
|
11
|
+
* caller using `validateMasterBearer` inherits the safer behaviour.
|
|
12
|
+
*
|
|
13
|
+
* Surface:
|
|
14
|
+
* `validateMasterBearer(authHeader, masterSecret) → ValidateMasterBearerResult`
|
|
15
|
+
*
|
|
16
|
+
* Errors are coarse-grained on purpose — callers should not surface the exact
|
|
17
|
+
* reason to remote clients beyond a generic 401/403.
|
|
18
|
+
*/
|
|
19
|
+
import type { ValidateMasterBearerResult } from "./types.js";
|
|
20
|
+
/**
|
|
21
|
+
* Validate an `Authorization: Bearer <token>` header against a configured
|
|
22
|
+
* master secret.
|
|
23
|
+
*
|
|
24
|
+
* - missing header → `{ok: false, error: "missing"}`
|
|
25
|
+
* - bad format / empty token / empty secret → `{ok: false, error: "malformed"}`
|
|
26
|
+
* - token sha256 != master sha256 (constant-time) → `{ok: false, error: "mismatch"}`
|
|
27
|
+
* - match → `{ok: true}`
|
|
28
|
+
*
|
|
29
|
+
* The scheme prefix match is case-insensitive (RFC 7235 §2.1) but the token
|
|
30
|
+
* itself is treated as opaque bytes.
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateMasterBearer(authHeader: string | undefined, masterSecret: string): Promise<ValidateMasterBearerResult>;
|
|
33
|
+
//# sourceMappingURL=bearer-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bearer-validation.d.ts","sourceRoot":"","sources":["../src/bearer-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAU7D;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,0BAA0B,CAAC,CAsBrC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Master-token Bearer validation.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the master-gate logic in
|
|
5
|
+
* `vantage-memory/mcp-server/server-http.ts` (`masterOnlyMiddleware`) and
|
|
6
|
+
* HARDENED per @vantageos/cloud-identity 0.1.0 contract:
|
|
7
|
+
* - SHA-256 hash both the presented token AND the configured master secret,
|
|
8
|
+
* then compare the two digests with `timingSafeEqual` (constant time).
|
|
9
|
+
* - The original middleware did a direct `token !== masterToken` string
|
|
10
|
+
* compare which is non-constant-time. This brick fixes that leak so any
|
|
11
|
+
* caller using `validateMasterBearer` inherits the safer behaviour.
|
|
12
|
+
*
|
|
13
|
+
* Surface:
|
|
14
|
+
* `validateMasterBearer(authHeader, masterSecret) → ValidateMasterBearerResult`
|
|
15
|
+
*
|
|
16
|
+
* Errors are coarse-grained on purpose — callers should not surface the exact
|
|
17
|
+
* reason to remote clients beyond a generic 401/403.
|
|
18
|
+
*/
|
|
19
|
+
import { timingSafeEqual } from "./crypto.js";
|
|
20
|
+
const BEARER_PREFIX_LOWER = "bearer ";
|
|
21
|
+
async function sha256Bytes(input) {
|
|
22
|
+
const data = new TextEncoder().encode(input);
|
|
23
|
+
const digest = await crypto.subtle.digest("SHA-256", data);
|
|
24
|
+
return new Uint8Array(digest);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validate an `Authorization: Bearer <token>` header against a configured
|
|
28
|
+
* master secret.
|
|
29
|
+
*
|
|
30
|
+
* - missing header → `{ok: false, error: "missing"}`
|
|
31
|
+
* - bad format / empty token / empty secret → `{ok: false, error: "malformed"}`
|
|
32
|
+
* - token sha256 != master sha256 (constant-time) → `{ok: false, error: "mismatch"}`
|
|
33
|
+
* - match → `{ok: true}`
|
|
34
|
+
*
|
|
35
|
+
* The scheme prefix match is case-insensitive (RFC 7235 §2.1) but the token
|
|
36
|
+
* itself is treated as opaque bytes.
|
|
37
|
+
*/
|
|
38
|
+
export async function validateMasterBearer(authHeader, masterSecret) {
|
|
39
|
+
if (authHeader === undefined || authHeader === null || authHeader === "") {
|
|
40
|
+
return { ok: false, error: "missing" };
|
|
41
|
+
}
|
|
42
|
+
if (!masterSecret || masterSecret.length === 0) {
|
|
43
|
+
return { ok: false, error: "malformed" };
|
|
44
|
+
}
|
|
45
|
+
// Case-insensitive scheme match without lower-casing the token bytes.
|
|
46
|
+
const lower = authHeader.toLowerCase();
|
|
47
|
+
if (!lower.startsWith(BEARER_PREFIX_LOWER)) {
|
|
48
|
+
return { ok: false, error: "malformed" };
|
|
49
|
+
}
|
|
50
|
+
const token = authHeader.slice(BEARER_PREFIX_LOWER.length).trim();
|
|
51
|
+
if (token.length === 0) {
|
|
52
|
+
return { ok: false, error: "malformed" };
|
|
53
|
+
}
|
|
54
|
+
const [tokenDigest, secretDigest] = await Promise.all([
|
|
55
|
+
sha256Bytes(token),
|
|
56
|
+
sha256Bytes(masterSecret),
|
|
57
|
+
]);
|
|
58
|
+
const ok = await timingSafeEqual(tokenDigest, secretDigest);
|
|
59
|
+
return ok ? { ok: true } : { ok: false, error: "mismatch" };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=bearer-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bearer-validation.js","sourceRoot":"","sources":["../src/bearer-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAEtC,KAAK,UAAU,WAAW,CAAC,KAAa;IACtC,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,UAA8B,EAC9B,YAAoB;IAEpB,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;QACzE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACzC,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IACD,sEAAsE;IACtE,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAClE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACpD,WAAW,CAAC,KAAK,CAAC;QAClB,WAAW,CAAC,YAAY,CAAC;KAC1B,CAAC,CAAC;IACH,MAAM,EAAE,GAAG,MAAM,eAAe,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAC5D,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AAC9D,CAAC"}
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constant-time byte-array comparison.
|
|
3
|
+
*
|
|
4
|
+
* Ported from `vantage-memory/mcp-server/src/crypto.ts` (Day 47 master-token
|
|
5
|
+
* gate, Eta F1 MAJOR PR #621). Surface adapted from `(string, string)` to
|
|
6
|
+
* `(Uint8Array, Uint8Array)` per @vantageos/cloud-identity 0.1.0 contract so
|
|
7
|
+
* the helper is framework-agnostic and reusable by any caller (hash bytes,
|
|
8
|
+
* raw token bytes, HMAC tags, etc.).
|
|
9
|
+
*
|
|
10
|
+
* Algorithm (identical to source):
|
|
11
|
+
* 1. Length mismatch → still run a dummy Web Crypto HMAC over equal-length
|
|
12
|
+
* input to avoid a branch-timing leak, then return false.
|
|
13
|
+
* 2. Equal length → XOR-accumulate diff across all bytes, return diff === 0.
|
|
14
|
+
*
|
|
15
|
+
* Web Crypto (`crypto.subtle`) is used for the dummy work (not Node's
|
|
16
|
+
* `crypto.timingSafeEqual`) for portability across runtimes (Node 20+, Bun,
|
|
17
|
+
* Deno, edge runtimes) and parity with the original Convex implementation.
|
|
18
|
+
*/
|
|
19
|
+
export declare function timingSafeEqual(a: Uint8Array, b: Uint8Array): Promise<boolean>;
|
|
20
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,CAAC,EAAE,UAAU,EACb,CAAC,EAAE,UAAU,GACZ,OAAO,CAAC,OAAO,CAAC,CAoBlB"}
|
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constant-time byte-array comparison.
|
|
3
|
+
*
|
|
4
|
+
* Ported from `vantage-memory/mcp-server/src/crypto.ts` (Day 47 master-token
|
|
5
|
+
* gate, Eta F1 MAJOR PR #621). Surface adapted from `(string, string)` to
|
|
6
|
+
* `(Uint8Array, Uint8Array)` per @vantageos/cloud-identity 0.1.0 contract so
|
|
7
|
+
* the helper is framework-agnostic and reusable by any caller (hash bytes,
|
|
8
|
+
* raw token bytes, HMAC tags, etc.).
|
|
9
|
+
*
|
|
10
|
+
* Algorithm (identical to source):
|
|
11
|
+
* 1. Length mismatch → still run a dummy Web Crypto HMAC over equal-length
|
|
12
|
+
* input to avoid a branch-timing leak, then return false.
|
|
13
|
+
* 2. Equal length → XOR-accumulate diff across all bytes, return diff === 0.
|
|
14
|
+
*
|
|
15
|
+
* Web Crypto (`crypto.subtle`) is used for the dummy work (not Node's
|
|
16
|
+
* `crypto.timingSafeEqual`) for portability across runtimes (Node 20+, Bun,
|
|
17
|
+
* Deno, edge runtimes) and parity with the original Convex implementation.
|
|
18
|
+
*/
|
|
19
|
+
export async function timingSafeEqual(a, b) {
|
|
20
|
+
if (a.length !== b.length) {
|
|
21
|
+
// Still do a comparison on equal-length buffers to avoid branch-timing leak.
|
|
22
|
+
const dummy = new Uint8Array(a.length);
|
|
23
|
+
const keyMaterial = a.length > 0 ? a : new Uint8Array([0]);
|
|
24
|
+
const aKey = await crypto.subtle.importKey("raw", keyMaterial, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
25
|
+
await crypto.subtle.sign("HMAC", aKey, dummy);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
let diff = 0;
|
|
29
|
+
for (let i = 0; i < a.length; i++) {
|
|
30
|
+
diff |= a[i] ^ b[i];
|
|
31
|
+
}
|
|
32
|
+
return diff === 0;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,CAAa,EACb,CAAa;IAEb,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QAC1B,6EAA6E;QAC7E,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACxC,KAAK,EACL,WAAsC,EACtC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAC;QACF,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAgC,CAAC,CAAC;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,CAAC;AACpB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vantageos/cloud-identity — public surface.
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic identity + scope-filter primitives for VantagePeers Cloud
|
|
5
|
+
* (multi-tenant MCP). Consumed by `vantage-peers-mcp` and
|
|
6
|
+
* `@vantageos/vantage-crm-mcp`.
|
|
7
|
+
*/
|
|
8
|
+
export { timingSafeEqual } from "./crypto.js";
|
|
9
|
+
export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, type ScopeFilterable, } from "./scope-filter.js";
|
|
10
|
+
export { validateMasterBearer } from "./bearer-validation.js";
|
|
11
|
+
export type { OAuthCtx, ScopeProfile, NamespacePrefix, FromAllowListEntry, ValidateMasterBearerResult, } from "./types.js";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vantageos/cloud-identity — public surface.
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic identity + scope-filter primitives for VantagePeers Cloud
|
|
5
|
+
* (multi-tenant MCP). Consumed by `vantage-peers-mcp` and
|
|
6
|
+
* `@vantageos/vantage-crm-mcp`.
|
|
7
|
+
*/
|
|
8
|
+
export { timingSafeEqual } from "./crypto.js";
|
|
9
|
+
export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, } from "./scope-filter.js";
|
|
10
|
+
export { validateMasterBearer } from "./bearer-validation.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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,GAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprint S3.1 B2 — scope-aware filter helpers for VP MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* Ported from `vantage-memory/mcp-server/src/scope-filter.ts`. Behaviour is
|
|
5
|
+
* preserved verbatim; only the type dependency on the in-tree `OAuthContext`
|
|
6
|
+
* is replaced by the framework-agnostic `OAuthCtx` from `./types`.
|
|
7
|
+
*
|
|
8
|
+
* Doctrine references:
|
|
9
|
+
* - decisions/doctrine-scope-aware-filter-2026-05-26.md (D3 base)
|
|
10
|
+
* - memory j579y6f31g7xzgtgdnpgetdmjx87ztyj (D9-D14 extension)
|
|
11
|
+
*
|
|
12
|
+
* Contract:
|
|
13
|
+
* - Master scope (scope === "master" OR fromAllowList includes "*") = wildcard pass.
|
|
14
|
+
* - Legacy bearer (oauthCtx === undefined) = wildcard pass (treated as master-
|
|
15
|
+
* equivalent for backward-compatibility with mcpTenants Pi/Tau/Phi paths).
|
|
16
|
+
* - Non-master scope = row passes iff:
|
|
17
|
+
* row.createdBy ∈ oauthCtx.fromAllowList
|
|
18
|
+
* OR
|
|
19
|
+
* row.namespace startsWith one of oauthCtx.namespaceReadPrefixes
|
|
20
|
+
* (prefix matched as exact-equal OR followed by '/' boundary)
|
|
21
|
+
* - Row missing BOTH `createdBy` and `namespace` = denied for non-master.
|
|
22
|
+
*
|
|
23
|
+
* NOTE: this module is intentionally framework-agnostic — no McpServer / Hono
|
|
24
|
+
* imports — to keep the helpers trivially unit-testable.
|
|
25
|
+
*/
|
|
26
|
+
import type { OAuthCtx } from "./types.js";
|
|
27
|
+
/**
|
|
28
|
+
* Row shape accepted by the scope filter. All fields optional because real
|
|
29
|
+
* Convex documents from list_peers / list_messages / etc. don't all carry both.
|
|
30
|
+
*/
|
|
31
|
+
export type ScopeFilterable = {
|
|
32
|
+
createdBy?: string;
|
|
33
|
+
namespace?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Returns true when the scope profile grants full, wildcard access.
|
|
37
|
+
* Mirrors `isMasterScope` from the source module: `scope === "master"` OR
|
|
38
|
+
* `fromAllowList` contains the `"*"` sentinel.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isMasterScope(ctx: OAuthCtx | undefined): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Core predicate. Returns true when the row is visible to the caller.
|
|
43
|
+
*
|
|
44
|
+
* - Master scope (isMasterScope === true) → true (wildcard)
|
|
45
|
+
* - Legacy bearer (oauthCtx === undefined) → true (back-compat)
|
|
46
|
+
* - row.createdBy ∈ oauthCtx.fromAllowList → true
|
|
47
|
+
* - row.namespace === prefix OR startsWith prefix+'/'
|
|
48
|
+
* for any prefix ∈ oauthCtx.namespaceReadPrefixes → true
|
|
49
|
+
* - otherwise → false
|
|
50
|
+
*
|
|
51
|
+
* Substring matches that don't fall on a '/' boundary are explicitly rejected
|
|
52
|
+
* (e.g. namespace="orchestrator/alphabet" does NOT match prefix
|
|
53
|
+
* "orchestrator/alpha"). This avoids the classic prefix-isolation bypass.
|
|
54
|
+
*/
|
|
55
|
+
export declare function passesScopeFilter(oauthCtx: OAuthCtx | undefined, row: ScopeFilterable): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Filter a list of rows (post-query). Used by list_* tools.
|
|
58
|
+
*/
|
|
59
|
+
export declare function scopeFilterList<T extends ScopeFilterable>(oauthCtx: OAuthCtx | undefined, rows: T[]): T[];
|
|
60
|
+
/**
|
|
61
|
+
* Assert a single row passes (get_* tools). Returns the row when allowed, null
|
|
62
|
+
* otherwise — callers translate null to a 404-equivalent "not found" MCP error
|
|
63
|
+
* to avoid leaking the difference between "absent" and "filtered out".
|
|
64
|
+
*/
|
|
65
|
+
export declare function scopeFilterGet<T extends ScopeFilterable>(oauthCtx: OAuthCtx | undefined, row: T | null | undefined): T | null;
|
|
66
|
+
/**
|
|
67
|
+
* Helper for callers that want a single "is master / legacy" predicate.
|
|
68
|
+
* Mirrors the legacy-bearer-passes-through convention.
|
|
69
|
+
*/
|
|
70
|
+
export declare function isWildcardScope(ctx: OAuthCtx | undefined): boolean;
|
|
71
|
+
//# sourceMappingURL=scope-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-filter.d.ts","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;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,SAAS,GAAG,OAAO,CAIhE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,GAAG,EAAE,eAAe,GACnB,OAAO,CAYT;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,eAAe,EACvD,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,IAAI,EAAE,CAAC,EAAE,GACR,CAAC,EAAE,CAEL;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,eAAe,EACtD,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACxB,CAAC,GAAG,IAAI,CAGV;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAGlE"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprint S3.1 B2 — scope-aware filter helpers for VP MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* Ported from `vantage-memory/mcp-server/src/scope-filter.ts`. Behaviour is
|
|
5
|
+
* preserved verbatim; only the type dependency on the in-tree `OAuthContext`
|
|
6
|
+
* is replaced by the framework-agnostic `OAuthCtx` from `./types`.
|
|
7
|
+
*
|
|
8
|
+
* Doctrine references:
|
|
9
|
+
* - decisions/doctrine-scope-aware-filter-2026-05-26.md (D3 base)
|
|
10
|
+
* - memory j579y6f31g7xzgtgdnpgetdmjx87ztyj (D9-D14 extension)
|
|
11
|
+
*
|
|
12
|
+
* Contract:
|
|
13
|
+
* - Master scope (scope === "master" OR fromAllowList includes "*") = wildcard pass.
|
|
14
|
+
* - Legacy bearer (oauthCtx === undefined) = wildcard pass (treated as master-
|
|
15
|
+
* equivalent for backward-compatibility with mcpTenants Pi/Tau/Phi paths).
|
|
16
|
+
* - Non-master scope = row passes iff:
|
|
17
|
+
* row.createdBy ∈ oauthCtx.fromAllowList
|
|
18
|
+
* OR
|
|
19
|
+
* row.namespace startsWith one of oauthCtx.namespaceReadPrefixes
|
|
20
|
+
* (prefix matched as exact-equal OR followed by '/' boundary)
|
|
21
|
+
* - Row missing BOTH `createdBy` and `namespace` = denied for non-master.
|
|
22
|
+
*
|
|
23
|
+
* NOTE: this module is intentionally framework-agnostic — no McpServer / Hono
|
|
24
|
+
* imports — to keep the helpers trivially unit-testable.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Returns true when the scope profile grants full, wildcard access.
|
|
28
|
+
* Mirrors `isMasterScope` from the source module: `scope === "master"` OR
|
|
29
|
+
* `fromAllowList` contains the `"*"` sentinel.
|
|
30
|
+
*/
|
|
31
|
+
export function isMasterScope(ctx) {
|
|
32
|
+
if (!ctx)
|
|
33
|
+
return false;
|
|
34
|
+
if (ctx.scope === "master")
|
|
35
|
+
return true;
|
|
36
|
+
return ctx.fromAllowList.includes("*");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Core predicate. Returns true when the row is visible to the caller.
|
|
40
|
+
*
|
|
41
|
+
* - Master scope (isMasterScope === true) → true (wildcard)
|
|
42
|
+
* - Legacy bearer (oauthCtx === undefined) → true (back-compat)
|
|
43
|
+
* - row.createdBy ∈ oauthCtx.fromAllowList → true
|
|
44
|
+
* - row.namespace === prefix OR startsWith prefix+'/'
|
|
45
|
+
* for any prefix ∈ oauthCtx.namespaceReadPrefixes → true
|
|
46
|
+
* - otherwise → false
|
|
47
|
+
*
|
|
48
|
+
* Substring matches that don't fall on a '/' boundary are explicitly rejected
|
|
49
|
+
* (e.g. namespace="orchestrator/alphabet" does NOT match prefix
|
|
50
|
+
* "orchestrator/alpha"). This avoids the classic prefix-isolation bypass.
|
|
51
|
+
*/
|
|
52
|
+
export function passesScopeFilter(oauthCtx, row) {
|
|
53
|
+
if (!oauthCtx)
|
|
54
|
+
return true;
|
|
55
|
+
if (isMasterScope(oauthCtx))
|
|
56
|
+
return true;
|
|
57
|
+
const { createdBy, namespace } = row;
|
|
58
|
+
if (createdBy && oauthCtx.fromAllowList.includes(createdBy))
|
|
59
|
+
return true;
|
|
60
|
+
if (namespace) {
|
|
61
|
+
for (const p of oauthCtx.namespaceReadPrefixes) {
|
|
62
|
+
if (namespace === p)
|
|
63
|
+
return true;
|
|
64
|
+
if (namespace.startsWith(`${p}/`))
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Filter a list of rows (post-query). Used by list_* tools.
|
|
72
|
+
*/
|
|
73
|
+
export function scopeFilterList(oauthCtx, rows) {
|
|
74
|
+
return rows.filter((r) => passesScopeFilter(oauthCtx, r));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Assert a single row passes (get_* tools). Returns the row when allowed, null
|
|
78
|
+
* otherwise — callers translate null to a 404-equivalent "not found" MCP error
|
|
79
|
+
* to avoid leaking the difference between "absent" and "filtered out".
|
|
80
|
+
*/
|
|
81
|
+
export function scopeFilterGet(oauthCtx, row) {
|
|
82
|
+
if (row == null)
|
|
83
|
+
return null;
|
|
84
|
+
return passesScopeFilter(oauthCtx, row) ? row : null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Helper for callers that want a single "is master / legacy" predicate.
|
|
88
|
+
* Mirrors the legacy-bearer-passes-through convention.
|
|
89
|
+
*/
|
|
90
|
+
export function isWildcardScope(ctx) {
|
|
91
|
+
if (!ctx)
|
|
92
|
+
return true;
|
|
93
|
+
return isMasterScope(ctx);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=scope-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-filter.js","sourceRoot":"","sources":["../src/scope-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAaH;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAyB;IACrD,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,IAAI,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAA8B,EAC9B,GAAoB;IAEpB,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,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,QAA8B,EAC9B,IAAS;IAET,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,QAA8B,EAC9B,GAAyB;IAEzB,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;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,GAAyB;IACvD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type surface for @vantageos/cloud-identity.
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic — no Hono, no Convex, no MCP SDK imports.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Scope profile name. Master = wildcard. Tenant = scoped via fromAllowList
|
|
8
|
+
* and namespace prefixes. Extensible string for future profiles.
|
|
9
|
+
*/
|
|
10
|
+
export type ScopeProfile = "master" | "tenant" | (string & {});
|
|
11
|
+
/**
|
|
12
|
+
* Namespace prefix (matched as exact-equal OR followed by '/' boundary).
|
|
13
|
+
*/
|
|
14
|
+
export type NamespacePrefix = string;
|
|
15
|
+
/**
|
|
16
|
+
* Identifier allowed on a row's `createdBy` field for non-master scopes.
|
|
17
|
+
*/
|
|
18
|
+
export type FromAllowListEntry = string;
|
|
19
|
+
/**
|
|
20
|
+
* Minimal OAuth context attached to an inbound MCP request.
|
|
21
|
+
*
|
|
22
|
+
* - `fromAllowList`: row.createdBy must be in this list for non-master callers
|
|
23
|
+
* to see a row. `["*"]` is the master wildcard convention.
|
|
24
|
+
* - `namespaceReadPrefixes`: row.namespace must equal one of these OR start
|
|
25
|
+
* with prefix + '/' for the row to be readable by non-master callers.
|
|
26
|
+
* - `namespaceWritePrefixes`: same semantics for write paths (enforced by the
|
|
27
|
+
* caller, this brick only exposes the field on the type).
|
|
28
|
+
* - `scope`: optional scope profile name. `"master"` triggers wildcard pass.
|
|
29
|
+
*/
|
|
30
|
+
export type OAuthCtx = {
|
|
31
|
+
fromAllowList: FromAllowListEntry[];
|
|
32
|
+
namespaceReadPrefixes: NamespacePrefix[];
|
|
33
|
+
namespaceWritePrefixes: NamespacePrefix[];
|
|
34
|
+
scope?: ScopeProfile;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Result of `validateMasterBearer`.
|
|
38
|
+
*
|
|
39
|
+
* - `ok: true` → token matched master secret in constant time.
|
|
40
|
+
* - `ok: false` with `error: "missing"` → no Authorization header.
|
|
41
|
+
* - `ok: false` with `error: "malformed"` → header present but not parseable
|
|
42
|
+
* as `Bearer <non-empty-token>`, OR master secret is empty.
|
|
43
|
+
* - `ok: false` with `error: "mismatch"` → token parsed but did not match.
|
|
44
|
+
*/
|
|
45
|
+
export type ValidateMasterBearerResult = {
|
|
46
|
+
ok: true;
|
|
47
|
+
error?: undefined;
|
|
48
|
+
} | {
|
|
49
|
+
ok: false;
|
|
50
|
+
error: "missing" | "malformed" | "mismatch";
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAExC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,qBAAqB,EAAE,eAAe,EAAE,CAAC;IACzC,sBAAsB,EAAE,eAAe,EAAE,CAAC;IAC1C,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,0BAA0B,GAClC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,GAC/B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,UAAU,CAAA;CAAE,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vantageos/cloud-identity",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Framework-agnostic identity + scope-filter primitives for VantagePeers Cloud (multi-tenant MCP).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./crypto": {
|
|
14
|
+
"types": "./dist/crypto.d.ts",
|
|
15
|
+
"import": "./dist/crypto.js"
|
|
16
|
+
},
|
|
17
|
+
"./scope-filter": {
|
|
18
|
+
"types": "./dist/scope-filter.d.ts",
|
|
19
|
+
"import": "./dist/scope-filter.js"
|
|
20
|
+
},
|
|
21
|
+
"./bearer-validation": {
|
|
22
|
+
"types": "./dist/bearer-validation.d.ts",
|
|
23
|
+
"import": "./dist/bearer-validation.js"
|
|
24
|
+
},
|
|
25
|
+
"./types": {
|
|
26
|
+
"types": "./dist/types.d.ts",
|
|
27
|
+
"import": "./dist/types.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"CHANGELOG.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest",
|
|
40
|
+
"lint": "tsc --noEmit",
|
|
41
|
+
"prepublishOnly": "npm run build && npm test"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"vantagepeers",
|
|
45
|
+
"mcp",
|
|
46
|
+
"oauth",
|
|
47
|
+
"scope",
|
|
48
|
+
"multi-tenant",
|
|
49
|
+
"identity"
|
|
50
|
+
],
|
|
51
|
+
"author": "VantagePeers Cloud",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/vantageos-agency/cloud-identity.git"
|
|
56
|
+
},
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/vantageos-agency/cloud-identity/issues"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://github.com/vantageos-agency/cloud-identity#readme",
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=20"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/node": "^20.11.0",
|
|
69
|
+
"typescript": "^5.4.0",
|
|
70
|
+
"vitest": "^1.6.0"
|
|
71
|
+
}
|
|
72
|
+
}
|