@jbctechsolutions/mcp-office365 4.2.1 → 4.3.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/README.md +9 -4
- package/dist/approval/types.d.ts +2 -2
- package/dist/approval/types.d.ts.map +1 -1
- package/dist/build-info.json +2 -2
- package/dist/cli.d.ts +14 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +55 -0
- package/dist/cli.js.map +1 -1
- package/dist/graph/client/graph-client.d.ts +15 -1
- package/dist/graph/client/graph-client.d.ts.map +1 -1
- package/dist/graph/client/graph-client.js +48 -2
- package/dist/graph/client/graph-client.js.map +1 -1
- package/dist/graph/planner-task-message-payload.d.ts +24 -0
- package/dist/graph/planner-task-message-payload.d.ts.map +1 -0
- package/dist/graph/planner-task-message-payload.js +34 -0
- package/dist/graph/planner-task-message-payload.js.map +1 -0
- package/dist/graph/repository.d.ts +35 -2
- package/dist/graph/repository.d.ts.map +1 -1
- package/dist/graph/repository.js +95 -4
- package/dist/graph/repository.js.map +1 -1
- package/dist/ids/next-action.d.ts.map +1 -1
- package/dist/ids/next-action.js +2 -1
- package/dist/ids/next-action.js.map +1 -1
- package/dist/ids/schema.d.ts +1 -0
- package/dist/ids/schema.d.ts.map +1 -1
- package/dist/ids/schema.js +2 -0
- package/dist/ids/schema.js.map +1 -1
- package/dist/ids/token.d.ts +1 -1
- package/dist/ids/token.d.ts.map +1 -1
- package/dist/ids/token.js +1 -0
- package/dist/ids/token.js.map +1 -1
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +211 -28
- package/dist/index.js.map +1 -1
- package/dist/registry/registry.d.ts +10 -0
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +25 -5
- package/dist/registry/registry.js.map +1 -1
- package/dist/remote/auth/deny-list.d.ts +29 -0
- package/dist/remote/auth/deny-list.d.ts.map +1 -0
- package/dist/remote/auth/deny-list.js +22 -0
- package/dist/remote/auth/deny-list.js.map +1 -0
- package/dist/remote/auth/metadata.d.ts +23 -0
- package/dist/remote/auth/metadata.d.ts.map +1 -0
- package/dist/remote/auth/metadata.js +28 -0
- package/dist/remote/auth/metadata.js.map +1 -0
- package/dist/remote/auth/middleware.d.ts +32 -0
- package/dist/remote/auth/middleware.d.ts.map +1 -0
- package/dist/remote/auth/middleware.js +82 -0
- package/dist/remote/auth/middleware.js.map +1 -0
- package/dist/remote/auth/obo.d.ts +38 -0
- package/dist/remote/auth/obo.d.ts.map +1 -0
- package/dist/remote/auth/obo.js +114 -0
- package/dist/remote/auth/obo.js.map +1 -0
- package/dist/remote/auth/verify.d.ts +43 -0
- package/dist/remote/auth/verify.d.ts.map +1 -0
- package/dist/remote/auth/verify.js +92 -0
- package/dist/remote/auth/verify.js.map +1 -0
- package/dist/remote/config.d.ts +33 -0
- package/dist/remote/config.d.ts.map +1 -0
- package/dist/remote/config.js +77 -0
- package/dist/remote/config.js.map +1 -0
- package/dist/remote/entitlements.d.ts +37 -0
- package/dist/remote/entitlements.d.ts.map +1 -0
- package/dist/remote/entitlements.js +146 -0
- package/dist/remote/entitlements.js.map +1 -0
- package/dist/remote/http-server.d.ts +65 -0
- package/dist/remote/http-server.d.ts.map +1 -0
- package/dist/remote/http-server.js +0 -0
- package/dist/remote/http-server.js.map +1 -0
- package/dist/remote/revocation.d.ts +38 -0
- package/dist/remote/revocation.d.ts.map +1 -0
- package/dist/remote/revocation.js +22 -0
- package/dist/remote/revocation.js.map +1 -0
- package/dist/state/schema.d.ts.map +1 -1
- package/dist/state/schema.js +10 -0
- package/dist/state/schema.js.map +1 -1
- package/dist/state/store.d.ts +19 -0
- package/dist/state/store.d.ts.map +1 -1
- package/dist/state/store.js +39 -0
- package/dist/state/store.js.map +1 -1
- package/dist/tools/planner.d.ts +71 -0
- package/dist/tools/planner.d.ts.map +1 -1
- package/dist/tools/planner.js +169 -0
- package/dist/tools/planner.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Entra JWT validation for remote mode (U4). Fail-closed member-only enforcement:
|
|
7
|
+
* a token is accepted only on a positive delegated-member signal, and any absent
|
|
8
|
+
* optional claim is treated as rejection — never as pass. Signature keys come
|
|
9
|
+
* from the configured tenant JWKS only (never the token's own `iss`).
|
|
10
|
+
*/
|
|
11
|
+
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
12
|
+
/** Clock skew tolerance for exp/nbf/iat (seconds) — tight on synced cloud clocks. */
|
|
13
|
+
const CLOCK_TOLERANCE_S = 120;
|
|
14
|
+
/** Token failed validation — respond 401 so the client re-authenticates. */
|
|
15
|
+
export class AuthChallengeError extends Error {
|
|
16
|
+
reason;
|
|
17
|
+
constructor(reason, message) {
|
|
18
|
+
super(message ?? `invalid_token: ${reason}`);
|
|
19
|
+
this.name = 'AuthChallengeError';
|
|
20
|
+
this.reason = reason;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Authenticated but not authorized (guest, app-only, wrong scope, deny-listed) — respond 403. */
|
|
24
|
+
export class AuthForbiddenError extends Error {
|
|
25
|
+
reason;
|
|
26
|
+
constructor(reason, message) {
|
|
27
|
+
super(message ?? `forbidden: ${reason}`);
|
|
28
|
+
this.name = 'AuthForbiddenError';
|
|
29
|
+
this.reason = reason;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Builds a verifier that resolves a bearer token to a {@link RemoteIdentity} or
|
|
34
|
+
* throws {@link AuthChallengeError} (401) / {@link AuthForbiddenError} (403).
|
|
35
|
+
*/
|
|
36
|
+
export function createTokenVerifier(config, keySource) {
|
|
37
|
+
// createRemoteJWKSet caches keys and refetches on an unknown `kid` with a
|
|
38
|
+
// built-in cooldown (bounds JWKS fetches; fails closed on outage).
|
|
39
|
+
const keys = keySource ?? createRemoteJWKSet(new URL(config.jwksUri));
|
|
40
|
+
return async function verify(token) {
|
|
41
|
+
let payload;
|
|
42
|
+
try {
|
|
43
|
+
const result = await jwtVerify(token, keys, {
|
|
44
|
+
issuer: config.issuer,
|
|
45
|
+
audience: [...config.allowedAudiences],
|
|
46
|
+
algorithms: ['RS256'], // pin RS256; reject none/HS*
|
|
47
|
+
clockTolerance: CLOCK_TOLERANCE_S,
|
|
48
|
+
});
|
|
49
|
+
payload = result.payload;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
// Signature, iss, aud, exp, nbf, alg failures — all token-invalid.
|
|
53
|
+
throw new AuthChallengeError('verification_failed', `invalid_token: ${error instanceof Error ? error.name : 'verification failed'}`);
|
|
54
|
+
}
|
|
55
|
+
// Fail-closed member-only enforcement — a positive signal is required.
|
|
56
|
+
if (payload.tid !== config.tenantId) {
|
|
57
|
+
throw new AuthForbiddenError('foreign_tenant');
|
|
58
|
+
}
|
|
59
|
+
// idtyp 'app' or a roles-without-scp shape = app-only token; reject.
|
|
60
|
+
if (payload.idtyp === 'app') {
|
|
61
|
+
throw new AuthForbiddenError('app_only_token');
|
|
62
|
+
}
|
|
63
|
+
const scopes = typeof payload.scp === 'string' ? payload.scp.split(' ').filter(Boolean) : [];
|
|
64
|
+
if (scopes.length === 0) {
|
|
65
|
+
// No delegated scope claim — not a delegated-user token. Fail closed.
|
|
66
|
+
throw new AuthForbiddenError('no_delegated_scope');
|
|
67
|
+
}
|
|
68
|
+
// acct: 0 = member, 1 = guest. Must be present AND 0.
|
|
69
|
+
if (payload.acct !== 0) {
|
|
70
|
+
throw new AuthForbiddenError('not_member');
|
|
71
|
+
}
|
|
72
|
+
if (!scopes.includes(config.requiredScope)) {
|
|
73
|
+
throw new AuthForbiddenError('insufficient_scope');
|
|
74
|
+
}
|
|
75
|
+
// Lowercase the identity so the deny-list key (written by the revoke CLI)
|
|
76
|
+
// and the durable-state account key match byte-for-byte regardless of the
|
|
77
|
+
// case an operator types; Entra GUIDs are canonically lowercase anyway.
|
|
78
|
+
const oid = typeof payload.oid === 'string' ? payload.oid.toLowerCase() : '';
|
|
79
|
+
const tid = typeof payload.tid === 'string' ? payload.tid.toLowerCase() : '';
|
|
80
|
+
if (oid === '' || tid === '') {
|
|
81
|
+
throw new AuthChallengeError('missing_identity');
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
oid,
|
|
85
|
+
tid,
|
|
86
|
+
homeAccountId: `${oid}.${tid}`,
|
|
87
|
+
scopes,
|
|
88
|
+
...(typeof payload.exp === 'number' ? { expiresAt: payload.exp } : {}),
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/remote/auth/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
|
+
*/
|
|
5
|
+
/** Resolved remote-mode auth configuration. */
|
|
6
|
+
export interface RemoteAuthConfig {
|
|
7
|
+
/** JP tenant GUID — sign-in is single-tenant to this directory. */
|
|
8
|
+
readonly tenantId: string;
|
|
9
|
+
/** The connector API app's client (GUID) — audience, and the OBO client id (U5). */
|
|
10
|
+
readonly apiClientId: string;
|
|
11
|
+
/** v2 token issuer that JWTs must carry. */
|
|
12
|
+
readonly issuer: string;
|
|
13
|
+
/** Tenant JWKS endpoint (signing keys). */
|
|
14
|
+
readonly jwksUri: string;
|
|
15
|
+
/** Accepted `aud` values: the API app's GUID and its Application ID URI. */
|
|
16
|
+
readonly allowedAudiences: readonly string[];
|
|
17
|
+
/** Canonical public MCP URL (RFC 8707 resource / RFC 9728 PRM resource). */
|
|
18
|
+
readonly publicUrl: string;
|
|
19
|
+
/** Delegated scope the token must carry. */
|
|
20
|
+
readonly requiredScope: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Loads and validates remote auth config from the environment. Throws (fail
|
|
24
|
+
* fast) on any missing or invalid value.
|
|
25
|
+
*/
|
|
26
|
+
export declare function loadRemoteAuthConfig(env?: NodeJS.ProcessEnv): RemoteAuthConfig;
|
|
27
|
+
/**
|
|
28
|
+
* Canonical RFC 8707 resource form: lowercase scheme/host, strip a trailing
|
|
29
|
+
* slash and default port. Path is preserved. claude.ai sends this exact form as
|
|
30
|
+
* `resource`, and the PRM `resource` must match it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function normalizeResourceUrl(raw: string): string;
|
|
33
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/remote/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAYD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAkC3F;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAkBxD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Remote-mode auth configuration (U4). Loaded once at `serve` startup with
|
|
7
|
+
* fail-fast diagnostics (the v4.2.0 pattern) so a misconfigured deployment
|
|
8
|
+
* refuses to start rather than silently accepting the wrong tokens.
|
|
9
|
+
*
|
|
10
|
+
* The server is a pure resource server: it validates Entra-issued JWTs whose
|
|
11
|
+
* audience is the connector's API app. It never mints tokens and holds no
|
|
12
|
+
* client secret here (the On-Behalf-Of credential lives in U5).
|
|
13
|
+
*/
|
|
14
|
+
const TENANT_PLACEHOLDERS = new Set(['common', 'organizations', 'consumers']);
|
|
15
|
+
const DEFAULT_APP_ID_URI = 'api://mcp-office365-connector';
|
|
16
|
+
function requireEnv(name, value) {
|
|
17
|
+
if (value == null || value.trim() === '') {
|
|
18
|
+
throw new Error(`${name} is required for remote (serve) mode. See docs/remote/provisioning.md ` +
|
|
19
|
+
`for the connector's app-registration values.`);
|
|
20
|
+
}
|
|
21
|
+
return value.trim();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Loads and validates remote auth config from the environment. Throws (fail
|
|
25
|
+
* fast) on any missing or invalid value.
|
|
26
|
+
*/
|
|
27
|
+
export function loadRemoteAuthConfig(env = process.env) {
|
|
28
|
+
// Entra issues `iss`/`tid` with a lowercase GUID; normalize so an uppercase
|
|
29
|
+
// env value can't silently break the issuer-string match (fail-closed denial).
|
|
30
|
+
const tenantId = requireEnv('OUTLOOK_MCP_TENANT_ID', env.OUTLOOK_MCP_TENANT_ID).toLowerCase();
|
|
31
|
+
if (TENANT_PLACEHOLDERS.has(tenantId)) {
|
|
32
|
+
throw new Error(`OUTLOOK_MCP_TENANT_ID must be a specific tenant GUID for remote mode, not ` +
|
|
33
|
+
`'${tenantId}'. Member-vs-guest enforcement requires a single directory.`);
|
|
34
|
+
}
|
|
35
|
+
const apiClientId = requireEnv('OUTLOOK_MCP_CONNECTOR_API_ID', env.OUTLOOK_MCP_CONNECTOR_API_ID);
|
|
36
|
+
const publicUrl = normalizeResourceUrl(requireEnv('OUTLOOK_MCP_CONNECTOR_URL', env.OUTLOOK_MCP_CONNECTOR_URL));
|
|
37
|
+
// An empty (whitespace-only) override must NOT become an accepted `''`
|
|
38
|
+
// audience — treat empty as unset and fall back to the app's identifier URI.
|
|
39
|
+
const appIdUriRaw = env.OUTLOOK_MCP_CONNECTOR_APP_ID_URI?.trim();
|
|
40
|
+
const appIdUri = appIdUriRaw != null && appIdUriRaw !== '' ? appIdUriRaw : DEFAULT_APP_ID_URI;
|
|
41
|
+
return {
|
|
42
|
+
tenantId,
|
|
43
|
+
apiClientId,
|
|
44
|
+
issuer: `https://login.microsoftonline.com/${tenantId}/v2.0`,
|
|
45
|
+
jwksUri: `https://login.microsoftonline.com/${tenantId}/discovery/v2.0/keys`,
|
|
46
|
+
// Accept both the bare GUID and the api:// identifier form (tokens may carry
|
|
47
|
+
// either); reject anything else (anti-token-passthrough).
|
|
48
|
+
allowedAudiences: [apiClientId, appIdUri],
|
|
49
|
+
publicUrl,
|
|
50
|
+
requiredScope: 'access_as_user',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Canonical RFC 8707 resource form: lowercase scheme/host, strip a trailing
|
|
55
|
+
* slash and default port. Path is preserved. claude.ai sends this exact form as
|
|
56
|
+
* `resource`, and the PRM `resource` must match it.
|
|
57
|
+
*/
|
|
58
|
+
export function normalizeResourceUrl(raw) {
|
|
59
|
+
let url;
|
|
60
|
+
try {
|
|
61
|
+
url = new URL(raw);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
throw new Error(`OUTLOOK_MCP_CONNECTOR_URL is not a valid URL: ${raw}`);
|
|
65
|
+
}
|
|
66
|
+
if (url.protocol !== 'https:' && url.hostname !== 'localhost' && url.hostname !== '127.0.0.1') {
|
|
67
|
+
throw new Error(`OUTLOOK_MCP_CONNECTOR_URL must be https (got ${url.protocol}//).`);
|
|
68
|
+
}
|
|
69
|
+
const port = (url.protocol === 'https:' && url.port === '443') ||
|
|
70
|
+
(url.protocol === 'http:' && url.port === '80')
|
|
71
|
+
? ''
|
|
72
|
+
: url.port;
|
|
73
|
+
const host = port === '' ? url.hostname.toLowerCase() : `${url.hostname.toLowerCase()}:${port}`;
|
|
74
|
+
const path = url.pathname === '/' ? '' : url.pathname.replace(/\/$/, '');
|
|
75
|
+
return `${url.protocol}//${host}${path}`;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/remote/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
|
+
*/
|
|
5
|
+
import type { Backend, SurfaceOptions } from '../registry/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* PINNED v1 default tool surface (R7): mail, calendar, files/SharePoint, and
|
|
8
|
+
* Planner — deliberately excluding shared-mailbox, mail-rules, and file-download
|
|
9
|
+
* / photo tools. Generated from the registry; update via a reviewed change when
|
|
10
|
+
* the default surface should shift (the contract test flags registry drift).
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_TOOL_SURFACE: readonly string[];
|
|
13
|
+
/** A single user's entitlement entry. */
|
|
14
|
+
export interface UserEntitlement {
|
|
15
|
+
/** Full surface (Joel's parity with local stdio) — ignores allow/default. */
|
|
16
|
+
readonly fullAccess?: boolean;
|
|
17
|
+
/** Explicit tool-name allow-list; overrides the pinned default. */
|
|
18
|
+
readonly allow?: readonly string[];
|
|
19
|
+
/** Tool names to remove (applies to full, default, or custom allow). */
|
|
20
|
+
readonly exclude?: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
/** Entitlement config file shape (keyed by Entra oid). */
|
|
23
|
+
export interface EntitlementConfig {
|
|
24
|
+
readonly version?: number;
|
|
25
|
+
readonly users?: Record<string, UserEntitlement>;
|
|
26
|
+
}
|
|
27
|
+
/** Resolves a user's tool-surface options. */
|
|
28
|
+
export interface EntitlementResolver {
|
|
29
|
+
resolve(oid: string, backend: Backend): SurfaceOptions;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Builds an entitlement resolver. When `configPath` is set, the file is read and
|
|
33
|
+
* re-read on mtime change (edits take effect on the next request). When unset,
|
|
34
|
+
* every user gets the pinned default surface.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createEntitlementResolver(configPath?: string): EntitlementResolver;
|
|
37
|
+
//# sourceMappingURL=entitlements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../../src/remote/entitlements.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAoChD,CAAC;AAEH,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAClD;AAED,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,cAAc,CAAC;CACxD;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,mBAAmB,CA8ClF"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Per-user tool entitlements (U6). A user's tool surface is resolved from a
|
|
7
|
+
* config file keyed by Entra `oid`, defaulting to a PINNED, explicit tool list.
|
|
8
|
+
*
|
|
9
|
+
* The default is an explicit allow-list (not a preset expansion) on purpose:
|
|
10
|
+
* because the surface is exactly these names, a server upgrade that adds new
|
|
11
|
+
* tools cannot silently widen any user's access — the pinned list must be edited
|
|
12
|
+
* (a reviewed jp-infrastructure change) for a new tool to become reachable. A
|
|
13
|
+
* contract test asserts every pinned name still exists in the registry so
|
|
14
|
+
* renames/removals surface as a failing build.
|
|
15
|
+
*
|
|
16
|
+
* Config is re-read on change (mtime), so an entitlement edit takes effect on a
|
|
17
|
+
* user's next request without a restart.
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
20
|
+
/**
|
|
21
|
+
* PINNED v1 default tool surface (R7): mail, calendar, files/SharePoint, and
|
|
22
|
+
* Planner — deliberately excluding shared-mailbox, mail-rules, and file-download
|
|
23
|
+
* / photo tools. Generated from the registry; update via a reviewed change when
|
|
24
|
+
* the default surface should shift (the contract test flags registry drift).
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_TOOL_SURFACE = Object.freeze([
|
|
27
|
+
'add_draft_attachment', 'add_draft_inline_image', 'check_availability', 'check_new_emails',
|
|
28
|
+
'clear_email_flag', 'confirm_archive_email', 'confirm_batch_operation', 'confirm_delete_bucket',
|
|
29
|
+
'confirm_delete_calendar_permission', 'confirm_delete_category', 'confirm_delete_drive_item',
|
|
30
|
+
'confirm_delete_email', 'confirm_delete_event', 'confirm_delete_focused_override',
|
|
31
|
+
'confirm_delete_folder', 'confirm_delete_list_item', 'confirm_delete_planner_task',
|
|
32
|
+
'confirm_empty_folder', 'confirm_forward_email', 'confirm_junk_email', 'confirm_move_email',
|
|
33
|
+
'confirm_reply_email', 'confirm_send_draft', 'confirm_send_email', 'confirm_upload_file',
|
|
34
|
+
'confirm_upload_library_file', 'create_bucket', 'create_calendar_group',
|
|
35
|
+
'create_calendar_permission', 'create_category', 'create_draft', 'create_event',
|
|
36
|
+
'create_focused_override', 'create_folder', 'create_library_folder', 'create_list',
|
|
37
|
+
'create_list_item', 'create_plan', 'create_planner_task', 'create_sharing_link', 'delete_event',
|
|
38
|
+
'find_meeting_times', 'forward_as_draft', 'generate_burndown_chart', 'generate_gantt_chart',
|
|
39
|
+
'generate_kanban_board', 'generate_plan_summary', 'get_automatic_replies', 'get_drive_item',
|
|
40
|
+
'get_email', 'get_emails', 'get_event', 'get_list', 'get_list_item', 'get_mail_tips',
|
|
41
|
+
'get_mailbox_settings', 'get_message_headers', 'get_message_mime', 'get_plan', 'get_planner_task',
|
|
42
|
+
'get_planner_task_details', 'get_signature', 'get_site', 'get_unread_count', 'list_attachments',
|
|
43
|
+
'list_buckets', 'list_calendar_groups', 'list_calendar_permissions', 'list_calendars',
|
|
44
|
+
'list_categories', 'list_conversation', 'list_document_libraries', 'list_drafts',
|
|
45
|
+
'list_drive_items', 'list_emails', 'list_event_instances', 'list_events', 'list_focused_overrides',
|
|
46
|
+
'list_folders', 'list_library_items', 'list_list_columns', 'list_list_items', 'list_lists',
|
|
47
|
+
'list_my_planner_tasks', 'list_planner_tasks', 'list_plans', 'list_recent_files', 'list_room_lists',
|
|
48
|
+
'list_rooms', 'list_shared_with_me', 'list_sites', 'mark_email_read', 'mark_email_unread',
|
|
49
|
+
'move_folder', 'prepare_archive_email', 'prepare_batch_delete_emails', 'prepare_batch_move_emails',
|
|
50
|
+
'prepare_delete_bucket', 'prepare_delete_calendar_permission', 'prepare_delete_category',
|
|
51
|
+
'prepare_delete_drive_item', 'prepare_delete_email', 'prepare_delete_event',
|
|
52
|
+
'prepare_delete_focused_override', 'prepare_delete_folder', 'prepare_delete_list_item',
|
|
53
|
+
'prepare_delete_planner_task', 'prepare_empty_folder', 'prepare_forward_email',
|
|
54
|
+
'prepare_junk_email', 'prepare_move_email', 'prepare_reply_email', 'prepare_send_draft',
|
|
55
|
+
'prepare_send_email', 'prepare_upload_file', 'prepare_upload_library_file', 'rename_folder',
|
|
56
|
+
'reply_as_draft', 'reset_change_tracking', 'respond_to_event', 'search_drive_items',
|
|
57
|
+
'search_emails', 'search_emails_advanced', 'search_events', 'search_sites', 'send_email',
|
|
58
|
+
'set_automatic_replies', 'set_email_categories', 'set_email_flag', 'set_email_importance',
|
|
59
|
+
'set_signature', 'update_bucket', 'update_draft', 'update_event', 'update_list_item',
|
|
60
|
+
'update_mailbox_settings', 'update_plan', 'update_planner_task', 'update_planner_task_details',
|
|
61
|
+
'what_changed',
|
|
62
|
+
]);
|
|
63
|
+
/**
|
|
64
|
+
* Builds an entitlement resolver. When `configPath` is set, the file is read and
|
|
65
|
+
* re-read on mtime change (edits take effect on the next request). When unset,
|
|
66
|
+
* every user gets the pinned default surface.
|
|
67
|
+
*/
|
|
68
|
+
export function createEntitlementResolver(configPath) {
|
|
69
|
+
let cached = {};
|
|
70
|
+
let cachedMtimeMs = -1;
|
|
71
|
+
function currentConfig() {
|
|
72
|
+
if (configPath == null)
|
|
73
|
+
return {};
|
|
74
|
+
let mtimeMs;
|
|
75
|
+
try {
|
|
76
|
+
mtimeMs = statSync(configPath).mtimeMs;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// File missing/unreadable → fail safe to the pinned default for everyone.
|
|
80
|
+
cached = {};
|
|
81
|
+
cachedMtimeMs = -1;
|
|
82
|
+
return cached;
|
|
83
|
+
}
|
|
84
|
+
if (mtimeMs !== cachedMtimeMs) {
|
|
85
|
+
// Advance the mtime marker regardless of outcome, so a persistently-
|
|
86
|
+
// malformed file is parsed + warned once per edit, not once per request.
|
|
87
|
+
cachedMtimeMs = mtimeMs;
|
|
88
|
+
try {
|
|
89
|
+
cached = parseConfig(readFileSync(configPath, 'utf-8'));
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
// Malformed config must not widen access — keep the last-good (or default)
|
|
93
|
+
// and warn; a bad edit fails safe rather than exposing the full surface.
|
|
94
|
+
process.stderr.write(`[mcp-office365] entitlement config parse failed (${e instanceof Error ? e.message : String(e)}); ` +
|
|
95
|
+
`keeping previous config.\n`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return cached;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
resolve(oid, backend) {
|
|
102
|
+
const entry = currentConfig().users?.[oid];
|
|
103
|
+
if (entry?.fullAccess === true) {
|
|
104
|
+
return { backend, ...(entry.exclude != null ? { exclude: entry.exclude } : {}) };
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
backend,
|
|
108
|
+
allow: entry?.allow ?? DEFAULT_TOOL_SURFACE,
|
|
109
|
+
...(entry?.exclude != null ? { exclude: entry.exclude } : {}),
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** Parses + shallow-validates the entitlement config JSON. */
|
|
115
|
+
function parseConfig(raw) {
|
|
116
|
+
const parsed = JSON.parse(raw);
|
|
117
|
+
if (parsed == null || typeof parsed !== 'object') {
|
|
118
|
+
throw new Error('entitlement config must be a JSON object');
|
|
119
|
+
}
|
|
120
|
+
const obj = parsed;
|
|
121
|
+
const users = {};
|
|
122
|
+
if (obj.users != null) {
|
|
123
|
+
if (typeof obj.users !== 'object')
|
|
124
|
+
throw new Error('`users` must be an object keyed by oid');
|
|
125
|
+
for (const [oid, value] of Object.entries(obj.users)) {
|
|
126
|
+
if (value == null || typeof value !== 'object') {
|
|
127
|
+
throw new Error(`users.${oid} must be an object`);
|
|
128
|
+
}
|
|
129
|
+
const v = value;
|
|
130
|
+
if (v.allow != null && !isStringArray(v.allow))
|
|
131
|
+
throw new Error(`users.${oid}.allow must be a string[]`);
|
|
132
|
+
if (v.exclude != null && !isStringArray(v.exclude))
|
|
133
|
+
throw new Error(`users.${oid}.exclude must be a string[]`);
|
|
134
|
+
users[oid] = {
|
|
135
|
+
...(v.fullAccess === true ? { fullAccess: true } : {}),
|
|
136
|
+
...(v.allow != null ? { allow: v.allow } : {}),
|
|
137
|
+
...(v.exclude != null ? { exclude: v.exclude } : {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { ...(typeof obj.version === 'number' ? { version: obj.version } : {}), users };
|
|
142
|
+
}
|
|
143
|
+
function isStringArray(value) {
|
|
144
|
+
return Array.isArray(value) && value.every((v) => typeof v === 'string');
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=entitlements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.js","sourceRoot":"","sources":["../../src/remote/entitlements.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
|
+
*/
|
|
5
|
+
import { type Express } from 'express';
|
|
6
|
+
import type { Server as HttpServer } from 'node:http';
|
|
7
|
+
import { type ServerOptions } from '../index.js';
|
|
8
|
+
import type { StateStore } from '../state/store.js';
|
|
9
|
+
import type { RemoteAuthConfig } from './config.js';
|
|
10
|
+
import type { DenyList } from './auth/deny-list.js';
|
|
11
|
+
import type { RemoteIdentity } from './auth/verify.js';
|
|
12
|
+
import type { OboClient } from './auth/obo.js';
|
|
13
|
+
import type { EntitlementResolver } from './entitlements.js';
|
|
14
|
+
/** Auth bundle for U4/U5/U6 — config, verifier, deny-list, OBO, entitlements. */
|
|
15
|
+
export interface RemoteAuthBundle {
|
|
16
|
+
readonly config: RemoteAuthConfig;
|
|
17
|
+
readonly verify: (token: string) => Promise<RemoteIdentity>;
|
|
18
|
+
readonly denyList: DenyList;
|
|
19
|
+
/** Per-user tool-surface resolver (U6). Resolved fresh per request. */
|
|
20
|
+
readonly entitlements: EntitlementResolver;
|
|
21
|
+
/**
|
|
22
|
+
* On-Behalf-Of client (U5). When present, an authenticated request's tool
|
|
23
|
+
* calls exchange the inbound token for a per-user Graph token. When absent
|
|
24
|
+
* (OBO credential not yet provisioned), the handshake still works but tool
|
|
25
|
+
* calls fail fast until the credential lands.
|
|
26
|
+
*/
|
|
27
|
+
readonly obo?: OboClient;
|
|
28
|
+
}
|
|
29
|
+
/** Options for {@link startHttpServer}. */
|
|
30
|
+
export interface HttpServerOptions {
|
|
31
|
+
/** Interface to bind (loopback only until U4 auth lands). */
|
|
32
|
+
readonly host: string;
|
|
33
|
+
/** TCP port to listen on. */
|
|
34
|
+
readonly port: number;
|
|
35
|
+
/** Tool-surface options threaded into each per-request MCP server. */
|
|
36
|
+
readonly serverOptions: ServerOptions;
|
|
37
|
+
/** Shared, process-scoped durable store backing approvals and aliases. */
|
|
38
|
+
readonly stateStore: StateStore;
|
|
39
|
+
/**
|
|
40
|
+
* Resource-server auth (U4). When present, `/mcp` requires a valid Entra JWT
|
|
41
|
+
* and the RFC 9728 discovery routes are served; a non-loopback bind is then
|
|
42
|
+
* permitted. When absent, the endpoint is loopback-only (unauthenticated).
|
|
43
|
+
*/
|
|
44
|
+
readonly auth?: RemoteAuthBundle;
|
|
45
|
+
/**
|
|
46
|
+
* Returns the actually-bound port. Set by {@link startHttpServer} after listen
|
|
47
|
+
* so DNS-rebinding `allowedHosts` are correct even when binding port 0
|
|
48
|
+
* (ephemeral). Falls back to the configured port when absent.
|
|
49
|
+
*/
|
|
50
|
+
readonly getBoundPort?: () => number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Builds the Express app that fronts the stateless Streamable HTTP transport.
|
|
54
|
+
* Exported for tests; production callers use {@link startHttpServer}, which
|
|
55
|
+
* enforces the loopback-bind guard this function does not.
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildHttpApp(options: HttpServerOptions): Express;
|
|
58
|
+
/**
|
|
59
|
+
* Starts the remote HTTP server. Resolves once the socket is listening.
|
|
60
|
+
*
|
|
61
|
+
* @throws if asked to bind a non-loopback interface before U4 auth is mounted —
|
|
62
|
+
* the endpoint must never be reachable off-host without authentication.
|
|
63
|
+
*/
|
|
64
|
+
export declare function startHttpServer(options: HttpServerOptions): Promise<HttpServer>;
|
|
65
|
+
//# sourceMappingURL=http-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../../src/remote/http-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH,OAAgB,EACd,KAAK,OAAO,EAKb,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5D,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;CAC1B;AAKD,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,MAAM,CAAC;CACtC;AAkCD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CA8IhE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CA0B/E"}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server.js","sourceRoot":"","sources":["../../src/remote/http-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAEnG,OAAO,OAMN,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAK/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAoBzE,sFAAsF;AACtF,MAAM,aAAa,GAAG,KAAK,CAAC;AA0B5B,yDAAyD;AACzD,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,WAAW,CAAC;AACxE,CAAC;AAED;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,OAA0B;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,EAAE,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IACtD,MAAM,KAAK,GAAG;QACZ,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE;QACzB,aAAa,IAAI,EAAE;QACnB,aAAa,IAAI,EAAE;QACnB,SAAS,IAAI,EAAE;KAChB,CAAC;IACF,0EAA0E;IAC1E,iFAAiF;IACjF,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,8CAA8C;QAChD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAA0B;IACrD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAEhD,8EAA8E;IAC9E,oCAAoC;IACpC,MAAM,aAAa,GAAkB;QACnC,GAAG,OAAO,CAAC,aAAa;QACxB,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC;IAEF,kEAAkE;IAClE,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAa,EAAE,GAAa,EAAQ,EAAE;QACzD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,8EAA8E;IAC9E,sDAAsD;IACtD,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;QAChC,MAAM,GAAG,GAAG,CAAC,IAAa,EAAE,GAAa,EAAQ,EAAE;YACjD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1D,CAAC,CAAC;QACF,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACvB,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ,MAAM,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,4EAA4E;IAC5E,kBAAkB;IAClB,MAAM,QAAQ,GACZ,OAAO,CAAC,IAAI,IAAI,IAAI;QAClB,CAAC,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzF,CAAC,CAAC,EAAE,CAAC;IAET,8EAA8E;IAC9E,uEAAuE;IACvE,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAiB,EAAE;QACjF,0EAA0E;QAC1E,yEAAyE;QACzE,8EAA8E;QAC9E,6DAA6D;QAC7D,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;QAC9B,wEAAwE;QACxE,4EAA4E;QAC5E,4EAA4E;QAC5E,wEAAwE;QACxE,MAAM,OAAO,GACX,OAAO,CAAC,IAAI,IAAI,IAAI;YAClB,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,GAAG,IAAI,eAAe,EAAE,OAAO,CAAC;YACxF,CAAC,CAAC,SAAS,CAAC;QAChB,MAAM,iBAAiB,GACrB,OAAO,CAAC,IAAI,IAAI,IAAI;YAClB,CAAC,CAAC;gBACE,GAAG,aAAa;gBAChB,UAAU,EAAE,IAAI;gBAChB,GAAG,CAAC,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3D,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,GAAG,CAAC,cAAc,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,IAAI;oBACtE,CAAC,CAAC;wBACE,UAAU,EAAE;4BACV,aAAa,EAAE,GAAG,CAAC,cAAc,CAAC,aAAa;4BAC/C,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK;4BACzB,GAAG;yBACJ;qBACF;oBACH,CAAC,CAAC,EAAE,CAAC;aACR;YACH,CAAC,CAAC,aAAa,CAAC;QACpB,MAAM,MAAM,GAAc,YAAY,CAAC,iBAAiB,CAAC,CAAC;QAC1D,yEAAyE;QACzE,sEAAsE;QACtE,oEAAoE;QACpE,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;YAClD,4BAA4B,EAAE,IAAI;YAClC,YAAY,EAAE,eAAe,CAAC,OAAO,CAAC;SACvC,CAAC,CAAC;QAEH,8EAA8E;QAC9E,2EAA2E;QAC3E,0EAA0E;QAC1E,mEAAmE;QACnE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,qEAAqE;YACrE,0EAA0E;YAC1E,8BAA8B;YAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,SAAsB,CAAC,CAAC;YAC7C,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,wCAAwC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CACnG,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,wBAAwB,EAAE;oBAC1D,EAAE,EAAE,IAAI;iBACT,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,qEAAqE;gBACrE,wDAAwD;gBACxD,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,gBAAgB,GAAG,CAAC,IAAa,EAAE,GAAa,EAAQ,EAAE;QAC9D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,qBAAqB,EAAE;YACvD,EAAE,EAAE,IAAI;SACT,CAAC,CAAC;IACL,CAAC,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAClC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAErC,8EAA8E;IAC9E,4EAA4E;IAC5E,qDAAqD;IACrD,GAAG,CAAC,GAAG,CAAC,CAAC,GAAY,EAAE,IAAa,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;QAC/E,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAI,GAAgD,CAAC,MAAM;eACjE,GAA+B,CAAC,UAAU;eAC3C,GAAG,CAAC;QACT,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YACtB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACL,IAAI,EAAE,CAAC,KAAK;gBACZ,OAAO,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,cAAc;aACrE;YACD,EAAE,EAAE,IAAI;SACT,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAA0B;IACxD,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CACb,oBAAoB,OAAO,CAAC,IAAI,8CAA8C;YAC5E,sFAAsF,CACzF,CAAC;IACJ,CAAC;IAED,sEAAsE;IACtE,mBAAmB;IACnB,IAAI,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7B,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC;IAExE,OAAO,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACjD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE;YAC7D,SAAS,GAAI,UAAU,CAAC,OAAO,EAAkB,CAAC,IAAI,CAAC;YACvD,2EAA2E;YAC3E,4EAA4E;YAC5E,UAAU,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC3C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAuC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;YAC/E,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Offboarding / revocation (U7). Purging a user's tokens alone does not stop
|
|
7
|
+
* access — claude.ai may still hold a valid Entra token and an OBO would
|
|
8
|
+
* silently re-mint. So `revoke` does both, deny-list FIRST:
|
|
9
|
+
*
|
|
10
|
+
* 1. Add the oid to the deny-list (the auth middleware rejects it before OBO).
|
|
11
|
+
* 2. Purge their durable state (approval tokens, aliases, delta cursors).
|
|
12
|
+
*
|
|
13
|
+
* Ordering matters: inserting the deny-list row before the purge closes the
|
|
14
|
+
* window where a request already past middleware could write fresh state after
|
|
15
|
+
* the purge — it would be rejected on its next call, and nothing new persists.
|
|
16
|
+
* Entra account disablement is the independent backstop (OBO then fails).
|
|
17
|
+
*/
|
|
18
|
+
import type { StateStore } from '../state/store.js';
|
|
19
|
+
/** Result of a revoke. */
|
|
20
|
+
export interface RevokeResult {
|
|
21
|
+
readonly oid: string;
|
|
22
|
+
readonly homeAccountId: string;
|
|
23
|
+
readonly purgedRows: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Revokes a user: deny-list first, then purge their per-account durable state.
|
|
27
|
+
* `homeAccountId` is `<oid>.<tenantId>` (the store's account key).
|
|
28
|
+
*/
|
|
29
|
+
export declare function revokeUser(store: StateStore, oid: string, homeAccountId: string, now: number, reason?: string): RevokeResult;
|
|
30
|
+
/** Re-admits a previously revoked user (removes the deny-list entry). */
|
|
31
|
+
export declare function readmitUser(store: StateStore, oid: string): boolean;
|
|
32
|
+
/** Lists revoked identities. */
|
|
33
|
+
export declare function listRevoked(store: StateStore): Array<{
|
|
34
|
+
oid: string;
|
|
35
|
+
reason: string | null;
|
|
36
|
+
deniedAt: number;
|
|
37
|
+
}>;
|
|
38
|
+
//# sourceMappingURL=revocation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revocation.d.ts","sourceRoot":"","sources":["../../src/remote/revocation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,0BAA0B;AAC1B,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,GACd,YAAY,CAId;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,gCAAgC;AAChC,wBAAgB,WAAW,CACzB,KAAK,EAAE,UAAU,GAChB,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAEjE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Revokes a user: deny-list first, then purge their per-account durable state.
|
|
7
|
+
* `homeAccountId` is `<oid>.<tenantId>` (the store's account key).
|
|
8
|
+
*/
|
|
9
|
+
export function revokeUser(store, oid, homeAccountId, now, reason) {
|
|
10
|
+
store.denyUser(oid, reason ?? null, now); // deny-list BEFORE purge (ordering)
|
|
11
|
+
const purgedRows = store.purgeAccount(homeAccountId);
|
|
12
|
+
return { oid, homeAccountId, purgedRows };
|
|
13
|
+
}
|
|
14
|
+
/** Re-admits a previously revoked user (removes the deny-list entry). */
|
|
15
|
+
export function readmitUser(store, oid) {
|
|
16
|
+
return store.readmitUser(oid);
|
|
17
|
+
}
|
|
18
|
+
/** Lists revoked identities. */
|
|
19
|
+
export function listRevoked(store) {
|
|
20
|
+
return store.listDenied();
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=revocation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revocation.js","sourceRoot":"","sources":["../../src/remote/revocation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAyBH;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,KAAiB,EACjB,GAAW,EACX,aAAqB,EACrB,GAAW,EACX,MAAe;IAEf,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,oCAAoC;IAC9E,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;IACrD,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AAC5C,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,GAAW;IACxD,OAAO,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,WAAW,CACzB,KAAiB;IAEjB,OAAO,KAAK,CAAC,UAAU,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/state/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AAEH,2EAA2E;AAC3E,eAAO,MAAM,UAAU,EAAE,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/state/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AAEH,2EAA2E;AAC3E,eAAO,MAAM,UAAU,EAAE,SAAS,MAAM,EA6DvC,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,cAAc,QAAoB,CAAC;AAEhD,iBAAiB;AACjB,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AACpD,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AAEpE,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,QAA2B,CAAC"}
|
package/dist/state/schema.js
CHANGED
|
@@ -65,6 +65,16 @@ export const MIGRATIONS = [
|
|
|
65
65
|
last_changed INTEGER NOT NULL,
|
|
66
66
|
PRIMARY KEY (account_id, resource, graph_id)
|
|
67
67
|
);
|
|
68
|
+
`,
|
|
69
|
+
// v2 → v3: remote-mode revocation deny-list (U7). Keyed by Entra oid; a
|
|
70
|
+
// denied oid is rejected in the auth middleware before OBO, so a still-valid
|
|
71
|
+
// claude.ai token can't silently re-onboard a revoked user.
|
|
72
|
+
`
|
|
73
|
+
CREATE TABLE IF NOT EXISTS deny_list (
|
|
74
|
+
oid TEXT PRIMARY KEY,
|
|
75
|
+
reason TEXT,
|
|
76
|
+
denied_at INTEGER NOT NULL
|
|
77
|
+
);
|
|
68
78
|
`,
|
|
69
79
|
];
|
|
70
80
|
/** The schema version this build expects (equals the migration count). */
|
package/dist/state/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/state/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AAEH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,UAAU,GAAsB;IAC3C,yCAAyC;IACzC;;;;;;;;;;;;;;;;;;;;;;;;GAwBC;IACD,4EAA4E;IAC5E,4EAA4E;IAC5E,yEAAyE;IACzE;;;;;;;;;;;;;;;;;;;;GAoBC;CACF,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC;AAEhD,iBAAiB;AACjB,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AACpD,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AAEpE,2EAA2E;AAC3E,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC"}
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/state/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AAEH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,UAAU,GAAsB;IAC3C,yCAAyC;IACzC;;;;;;;;;;;;;;;;;;;;;;;;GAwBC;IACD,4EAA4E;IAC5E,4EAA4E;IAC5E,yEAAyE;IACzE;;;;;;;;;;;;;;;;;;;;GAoBC;IACD,wEAAwE;IACxE,6EAA6E;IAC7E,4DAA4D;IAC5D;;;;;;GAMC;CACF,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC;AAEhD,iBAAiB;AACjB,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AACpD,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AAEpE,2EAA2E;AAC3E,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC"}
|