@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
|
@@ -108,7 +108,7 @@ export class ToolRegistry {
|
|
|
108
108
|
// (onElicit), when the server is in elicit mode and the client can elicit.
|
|
109
109
|
// Everything else runs the handler directly — an unchanged fast path.
|
|
110
110
|
if (def.onElicit != null && ctx.confirmMode === 'elicit' && ctx.elicit != null) {
|
|
111
|
-
return this.dispatchWithElicitation(def, params, ctx, def.onElicit, ctx.elicit);
|
|
111
|
+
return this.dispatchWithElicitation(def, params, ctx, def.onElicit, ctx.elicit, options);
|
|
112
112
|
}
|
|
113
113
|
return def.handler(ctx, params);
|
|
114
114
|
}
|
|
@@ -122,7 +122,7 @@ export class ToolRegistry {
|
|
|
122
122
|
*
|
|
123
123
|
* Fail-closed: the destructive action runs only on an explicit accept.
|
|
124
124
|
*/
|
|
125
|
-
async dispatchWithElicitation(def, prepareParams, ctx, link, elicit) {
|
|
125
|
+
async dispatchWithElicitation(def, prepareParams, ctx, link, elicit, options) {
|
|
126
126
|
const prepareResult = await def.handler(ctx, prepareParams);
|
|
127
127
|
const tokenIds = link.collectTokenIds(prepareResult);
|
|
128
128
|
// No token in the prepare output → nothing to gate/execute; degrade.
|
|
@@ -138,6 +138,12 @@ export class ToolRegistry {
|
|
|
138
138
|
if (confirmDef == null) {
|
|
139
139
|
return prepareResult;
|
|
140
140
|
}
|
|
141
|
+
// Re-check the confirm tool against the surface (U6): a user whose
|
|
142
|
+
// entitlement excludes confirm_* must not execute it via inline accept.
|
|
143
|
+
// Degrade to the token flow (which dispatch() also blocks) rather than run.
|
|
144
|
+
if (!this.matches(confirmDef, options)) {
|
|
145
|
+
return prepareResult;
|
|
146
|
+
}
|
|
141
147
|
// Only the param mapping/validation is guarded: a bad buildParams must
|
|
142
148
|
// degrade (return the still-live token) rather than error out an accepted
|
|
143
149
|
// action. The confirm handler runs OUTSIDE the guard so a genuine
|
|
@@ -172,11 +178,25 @@ export class ToolRegistry {
|
|
|
172
178
|
if (options.readOnly === true && !isReadOnly(def)) {
|
|
173
179
|
return false;
|
|
174
180
|
}
|
|
181
|
+
// Exclusion wins over any inclusion (U6).
|
|
182
|
+
if (options.exclude != null && options.exclude.includes(def.name)) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
// Preset filter — the process-wide outer bound (`serve --preset`). A
|
|
186
|
+
// domain-less (empty-presets) tool is not subject to a domain cap. This
|
|
187
|
+
// composes with the per-user allow-list below, so an entitlement can only
|
|
188
|
+
// narrow within the preset cap, never exceed it.
|
|
175
189
|
const presets = options.presets;
|
|
176
|
-
|
|
177
|
-
|
|
190
|
+
const presetOk = presets == null || presets.length === 0 || def.presets.length === 0
|
|
191
|
+
? true
|
|
192
|
+
: def.presets.some((p) => presets.includes(p));
|
|
193
|
+
// Explicit allow-list mode (U6): exactly the listed tools (still capped by
|
|
194
|
+
// presets and read-only above); the empty-presets always-exposed bypass
|
|
195
|
+
// does not apply — a tool shows iff it is listed.
|
|
196
|
+
if (options.allow != null) {
|
|
197
|
+
return options.allow.includes(def.name) && presetOk;
|
|
178
198
|
}
|
|
179
|
-
return
|
|
199
|
+
return presetOk;
|
|
180
200
|
}
|
|
181
201
|
}
|
|
182
202
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/registry/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/registry/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AA8BvD;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,MAAiB;IAC7C,4EAA4E;IAC5E,yEAAyE;IACzE,6EAA6E;IAC7E,0DAA0D;IAC1D,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,CAA4B,CAAC;IACnG,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC;IACvB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACzB,IAAI,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;IAC1B,CAAC;IACD,OAAO,IAA2B,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,YAAY;IACN,WAAW,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEjE,yEAAyE;IACzE,QAAQ,CAAC,IAA+B;QACtC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;YAChE,CAAC;YACD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,qDAAqD;IACrD,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,uDAAuD;IACvD,KAAK;QACH,OAAO,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,iDAAiD;IACjD,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,OAAuB;QAC/B,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACxC,MAAM,IAAI,GAAS;gBACjB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,WAAW,EAAE,GAAG,CAAC,WAAW;gBAC5B,WAAW,EAAE,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;aACtC,CAAC;YACF,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5C,OAAO,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC;YACnD,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,2DAA2D;IAC3D,SAAS,CAAC,IAAY,EAAE,OAAuB;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,QAAQ,CACZ,IAAY,EACZ,IAAa,EACb,GAAgB,EAChB,OAAuB;QAEvB,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;YAChB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC;YAChC,8DAA8D;YAC9D,wEAAwE;YACxE,wEAAwE;YACxE,kEAAkE;YAClE,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;gBACpF,MAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACpC,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,yCAAyC,CAAC,CAAC;QAC1E,CAAC;QACD,kEAAkE;QAClE,mEAAmE;QACnE,kCAAkC;QAClC,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAE3C,8EAA8E;QAC9E,2EAA2E;QAC3E,sEAAsE;QACtE,IAAI,GAAG,CAAC,QAAQ,IAAI,IAAI,IAAI,GAAG,CAAC,WAAW,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;YAC/E,OAAO,IAAI,CAAC,uBAAuB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAED;;;;;;;;;OASG;IACK,KAAK,CAAC,uBAAuB,CACnC,GAAmB,EACnB,aAAsB,EACtB,GAAgB,EAChB,IAAgB,EAChB,MAAgB,EAChB,OAAuB;QAEvB,MAAM,aAAa,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;QACrD,qEAAqE;QACrE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,aAAa,CAAC;QACvB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,aAAa,CAAC,IAAI,qBAAqB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACzF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAE1C,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YACzB,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1D,yEAAyE;YACzE,mEAAmE;YACnE,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;gBACvB,OAAO,aAAa,CAAC;YACvB,CAAC;YACD,mEAAmE;YACnE,wEAAwE;YACxE,4EAA4E;YAC5E,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC;gBACvC,OAAO,aAAa,CAAC;YACvB,CAAC;YACD,uEAAuE;YACvE,0EAA0E;YAC1E,kEAAkE;YAClE,wEAAwE;YACxE,IAAI,aAAsB,CAAC;YAC3B,IAAI,CAAC;gBACH,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC;YACzF,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,aAAa,CAAC;YACvB,CAAC;YACD,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAChD,CAAC;QAED,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,wEAAwE;YACxE,oEAAoE;YACpE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAChC,CAAC;YACD,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;QAED,2EAA2E;QAC3E,OAAO,aAAa,CAAC;IACvB,CAAC;IAEO,QAAQ,CAAC,OAAuB;QACtC,OAAO,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IACpF,CAAC;IAEO,OAAO,CAAC,GAAmB,EAAE,OAAuB;QAC1D,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,0CAA0C;QAC1C,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAClE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,qEAAqE;QACrE,wEAAwE;QACxE,0EAA0E;QAC1E,iDAAiD;QACjD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,MAAM,QAAQ,GACZ,OAAO,IAAI,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YACjE,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,2EAA2E;QAC3E,wEAAwE;QACxE,kDAAkD;QAClD,IAAI,OAAO,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;YAC1B,OAAO,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;QACtD,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,GAAmB;IACrC,OAAO,GAAG,CAAC,WAAW,CAAC,YAAY,KAAK,IAAI,CAAC;AAC/C,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,GAAgB,EAAE,OAAe;IACxD,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QAClB,GAAG,CAAC,YAAY,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,SAAS,qBAAqB,CAAC,GAAgB,EAAE,OAA2B;IAC1E,MAAM,EAAE,GAAG,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1F,MAAM,MAAM,GAAG,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;IAClE,OAAO,YAAY,MAAM,0BAA0B,CAAC;AACtD,CAAC;AAED,uEAAuE;AACvE,SAAS,cAAc,CAAC,QAAgB;IACtC,OAAO;QACL,OAAO,EAAE,CAAC;gBACR,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,OAAO,EAAE,KAAK;oBACd,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE,uDAAuD,QAAQ,4CAA4C;iBACrH,EAAE,IAAI,EAAE,CAAC,CAAC;aACZ,CAAC;KACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
* Revocation deny-list (U4 reads it; U7 fills it). Purging a user's tokens alone
|
|
7
|
+
* does not stop access — claude.ai may still hold a valid Entra token and an OBO
|
|
8
|
+
* would silently re-mint. The middleware rejects deny-listed identities before
|
|
9
|
+
* any OBO, per request from the store (never process-cached, so a later
|
|
10
|
+
* perf-motivated cache can't reintroduce a staleness window).
|
|
11
|
+
*/
|
|
12
|
+
import type { StateStore } from '../../state/store.js';
|
|
13
|
+
/** Checks whether an Entra object id is revoked. */
|
|
14
|
+
export interface DenyList {
|
|
15
|
+
isDenied(oid: string): boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Store-backed deny-list (U7). Reads the durable deny-list per call — never
|
|
19
|
+
* process-cached — so a revocation takes effect on the next request and a later
|
|
20
|
+
* perf cache can't reintroduce a staleness window.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createStoreDenyList(store: StateStore): DenyList;
|
|
23
|
+
/**
|
|
24
|
+
* In-memory stub used until U7 wires a store-backed deny-list. Always empty —
|
|
25
|
+
* no user is revoked yet — but present so the middleware's rejection path exists
|
|
26
|
+
* and is testable from day one.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createStubDenyList(): DenyList;
|
|
29
|
+
//# sourceMappingURL=deny-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deny-list.d.ts","sourceRoot":"","sources":["../../../src/remote/auth/deny-list.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,oDAAoD;AACpD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,QAAQ,CAG7C"}
|
|
@@ -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
|
+
* Store-backed deny-list (U7). Reads the durable deny-list per call — never
|
|
7
|
+
* process-cached — so a revocation takes effect on the next request and a later
|
|
8
|
+
* perf cache can't reintroduce a staleness window.
|
|
9
|
+
*/
|
|
10
|
+
export function createStoreDenyList(store) {
|
|
11
|
+
return { isDenied: (oid) => store.isDenied(oid) };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* In-memory stub used until U7 wires a store-backed deny-list. Always empty —
|
|
15
|
+
* no user is revoked yet — but present so the middleware's rejection path exists
|
|
16
|
+
* and is testable from day one.
|
|
17
|
+
*/
|
|
18
|
+
export function createStubDenyList() {
|
|
19
|
+
const denied = new Set();
|
|
20
|
+
return { isDenied: (oid) => denied.has(oid) };
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=deny-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deny-list.js","sourceRoot":"","sources":["../../../src/remote/auth/deny-list.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAiBH;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAiB;IACnD,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAW,EAAW,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAW,EAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
* RFC 9728 Protected Resource Metadata + the 401 `WWW-Authenticate` challenge
|
|
7
|
+
* (U4). This is the discovery handshake a claude.ai custom connector follows:
|
|
8
|
+
* an unauthenticated request gets a 401 pointing at the PRM document, which in
|
|
9
|
+
* turn points at the Entra authorization server. All values come from config —
|
|
10
|
+
* never from request headers (a `Host`-header value here would let a caller
|
|
11
|
+
* poison discovery).
|
|
12
|
+
*/
|
|
13
|
+
import type { RemoteAuthConfig } from '../config.js';
|
|
14
|
+
/** RFC 9728 metadata path, and the path-suffixed variant claude.ai also probes. */
|
|
15
|
+
export declare const PRM_PATH = "/.well-known/oauth-protected-resource";
|
|
16
|
+
/** The Protected Resource Metadata document (RFC 9728). */
|
|
17
|
+
export declare function protectedResourceMetadata(config: RemoteAuthConfig): Record<string, unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* The `WWW-Authenticate` header value for a 401, carrying the PRM URL so the
|
|
20
|
+
* client can discover the authorization server (RFC 9728 §5.1).
|
|
21
|
+
*/
|
|
22
|
+
export declare function wwwAuthenticate(config: RemoteAuthConfig, error?: string): string;
|
|
23
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../../src/remote/auth/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,mFAAmF;AACnF,eAAO,MAAM,QAAQ,0CAA0C,CAAC;AAEhE,2DAA2D;AAC3D,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQ3F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
|
+
*/
|
|
5
|
+
/** RFC 9728 metadata path, and the path-suffixed variant claude.ai also probes. */
|
|
6
|
+
export const PRM_PATH = '/.well-known/oauth-protected-resource';
|
|
7
|
+
/** The Protected Resource Metadata document (RFC 9728). */
|
|
8
|
+
export function protectedResourceMetadata(config) {
|
|
9
|
+
return {
|
|
10
|
+
resource: config.publicUrl,
|
|
11
|
+
// claude.ai uses only the first entry; pin it to the JP tenant issuer.
|
|
12
|
+
authorization_servers: [config.issuer],
|
|
13
|
+
scopes_supported: [config.requiredScope],
|
|
14
|
+
bearer_methods_supported: ['header'],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The `WWW-Authenticate` header value for a 401, carrying the PRM URL so the
|
|
19
|
+
* client can discover the authorization server (RFC 9728 §5.1).
|
|
20
|
+
*/
|
|
21
|
+
export function wwwAuthenticate(config, error) {
|
|
22
|
+
const params = [`resource_metadata="${config.publicUrl}${PRM_PATH}"`];
|
|
23
|
+
if (error != null) {
|
|
24
|
+
params.unshift(`error="${error}"`);
|
|
25
|
+
}
|
|
26
|
+
return `Bearer ${params.join(', ')}`;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../src/remote/auth/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,mFAAmF;AACnF,MAAM,CAAC,MAAM,QAAQ,GAAG,uCAAuC,CAAC;AAEhE,2DAA2D;AAC3D,MAAM,UAAU,yBAAyB,CAAC,MAAwB;IAChE,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,SAAS;QAC1B,uEAAuE;QACvE,qBAAqB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;QACtC,gBAAgB,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC;QACxC,wBAAwB,EAAE,CAAC,QAAQ,CAAC;KACrC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAwB,EAAE,KAAc;IACtE,MAAM,MAAM,GAAG,CAAC,sBAAsB,MAAM,CAAC,SAAS,GAAG,QAAQ,GAAG,CAAC,CAAC;IACtE,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QAClB,MAAM,CAAC,OAAO,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,UAAU,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
* Bearer-auth middleware for `/mcp` (U4). Verifies the Entra JWT on every
|
|
7
|
+
* request (never trusts a session for auth), rejects deny-listed identities, and
|
|
8
|
+
* attaches an AuthInfo-compatible identity to the request for downstream units
|
|
9
|
+
* (U5 OBO). Authorization denials are logged as security events — never with any
|
|
10
|
+
* token material — as the only detection surface during the pilot.
|
|
11
|
+
*/
|
|
12
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
13
|
+
import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
|
|
14
|
+
import type { RemoteAuthConfig } from '../config.js';
|
|
15
|
+
import type { DenyList } from './deny-list.js';
|
|
16
|
+
import { type RemoteIdentity } from './verify.js';
|
|
17
|
+
/** Attach the validated identity (and SDK-compatible auth info) to the request. */
|
|
18
|
+
declare module 'express-serve-static-core' {
|
|
19
|
+
interface Request {
|
|
20
|
+
remoteIdentity?: RemoteIdentity;
|
|
21
|
+
auth?: AuthInfo;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
type Verify = (token: string) => Promise<RemoteIdentity>;
|
|
25
|
+
/**
|
|
26
|
+
* Express middleware requiring a valid, member, non-deny-listed Entra token.
|
|
27
|
+
* A missing/invalid token → 401 + WWW-Authenticate (RFC 9728 discovery); an
|
|
28
|
+
* authenticated-but-unauthorized identity → 403 (no re-auth loop).
|
|
29
|
+
*/
|
|
30
|
+
export declare function createAuthMiddleware(config: RemoteAuthConfig, verify: Verify, denyList: DenyList): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/remote/auth/middleware.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAA0C,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAG1F,mFAAmF;AACnF,OAAO,QAAQ,2BAA2B,CAAC;IACzC,UAAU,OAAO;QACf,cAAc,CAAC,EAAE,cAAc,CAAC;QAChC,IAAI,CAAC,EAAE,QAAQ,CAAC;KACjB;CACF;AAED,KAAK,MAAM,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;AAyBzD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,IAEJ,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAiD9E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
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 { AuthChallengeError, AuthForbiddenError } from './verify.js';
|
|
6
|
+
import { wwwAuthenticate } from './metadata.js';
|
|
7
|
+
/** Logs an authorization denial (reason + oid when known, no token material). */
|
|
8
|
+
function logDenial(reason, oid) {
|
|
9
|
+
const who = oid != null ? ` oid=${oid}` : '';
|
|
10
|
+
process.stderr.write(`[mcp-office365] auth denied: reason=${reason}${who} at=${new Date().toISOString()}\n`);
|
|
11
|
+
}
|
|
12
|
+
function challenge(res, config, reason) {
|
|
13
|
+
logDenial(reason);
|
|
14
|
+
res
|
|
15
|
+
.status(401)
|
|
16
|
+
.set('WWW-Authenticate', wwwAuthenticate(config, 'invalid_token'))
|
|
17
|
+
.json({ jsonrpc: '2.0', error: { code: -32001, message: 'Unauthorized.' }, id: null });
|
|
18
|
+
}
|
|
19
|
+
function forbidden(res, reason, oid) {
|
|
20
|
+
logDenial(reason, oid);
|
|
21
|
+
res
|
|
22
|
+
.status(403)
|
|
23
|
+
.json({ jsonrpc: '2.0', error: { code: -32002, message: 'Forbidden.' }, id: null });
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Express middleware requiring a valid, member, non-deny-listed Entra token.
|
|
27
|
+
* A missing/invalid token → 401 + WWW-Authenticate (RFC 9728 discovery); an
|
|
28
|
+
* authenticated-but-unauthorized identity → 403 (no re-auth loop).
|
|
29
|
+
*/
|
|
30
|
+
export function createAuthMiddleware(config, verify, denyList) {
|
|
31
|
+
return async (req, res, next) => {
|
|
32
|
+
// Whole-body guard: any unexpected throw (incl. a future store-backed
|
|
33
|
+
// deny-list in U7) fails closed to 401 rather than becoming an uncaught
|
|
34
|
+
// rejection that hangs the request.
|
|
35
|
+
try {
|
|
36
|
+
const header = req.headers.authorization;
|
|
37
|
+
// RFC 6750 auth-scheme is case-insensitive; trim the token.
|
|
38
|
+
const match = header != null ? /^Bearer[ ]+(.+)$/i.exec(header) : null;
|
|
39
|
+
const token = match?.[1]?.trim();
|
|
40
|
+
if (token == null || token === '') {
|
|
41
|
+
challenge(res, config, 'missing_token');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
let identity;
|
|
45
|
+
try {
|
|
46
|
+
identity = await verify(token);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof AuthForbiddenError) {
|
|
50
|
+
forbidden(res, error.reason);
|
|
51
|
+
}
|
|
52
|
+
else if (error instanceof AuthChallengeError) {
|
|
53
|
+
challenge(res, config, error.reason);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
challenge(res, config, 'verification_error');
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Deny-list is read per request from the store (U7) — never process-cached.
|
|
61
|
+
if (denyList.isDenied(identity.oid)) {
|
|
62
|
+
forbidden(res, 'deny_listed', identity.oid);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
req.remoteIdentity = identity;
|
|
66
|
+
// AuthInfo-compatible shape the SDK exposes to tool handlers as extra.authInfo.
|
|
67
|
+
req.auth = {
|
|
68
|
+
token,
|
|
69
|
+
clientId: '',
|
|
70
|
+
scopes: [...identity.scopes],
|
|
71
|
+
...(identity.expiresAt != null ? { expiresAt: identity.expiresAt } : {}),
|
|
72
|
+
resource: new URL(config.publicUrl),
|
|
73
|
+
extra: { oid: identity.oid, tid: identity.tid, homeAccountId: identity.homeAccountId },
|
|
74
|
+
};
|
|
75
|
+
next();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
challenge(res, config, 'auth_error');
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/remote/auth/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
+
import type { RemoteAuthConfig } from '../config.js';
|
|
6
|
+
import { type OutlookMcpError } from '../../utils/errors.js';
|
|
7
|
+
/** Confidential-client credential for OBO. Certificate is the production choice. */
|
|
8
|
+
export type OboCredential = {
|
|
9
|
+
readonly kind: 'secret';
|
|
10
|
+
readonly clientSecret: string;
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: 'certificate';
|
|
13
|
+
readonly thumbprint: string;
|
|
14
|
+
readonly privateKey: string;
|
|
15
|
+
};
|
|
16
|
+
/** Exchanges a validated inbound assertion for a per-user Graph token. */
|
|
17
|
+
export interface OboClient {
|
|
18
|
+
acquireGraphToken(userAssertion: string): Promise<string>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Loads the OBO credential from the environment, preferring a certificate.
|
|
22
|
+
* Returns null when none is configured (OBO disabled — U4 auth still works, but
|
|
23
|
+
* tool calls fail fast until the credential is provisioned in U9).
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadOboCredential(env?: NodeJS.ProcessEnv): OboCredential | null;
|
|
26
|
+
/**
|
|
27
|
+
* Builds the process-wide OBO client (one confidential-client app; per-user
|
|
28
|
+
* isolation is per-assertion, not per-instance).
|
|
29
|
+
*/
|
|
30
|
+
export declare function createOboClient(config: RemoteAuthConfig, credential: OboCredential): OboClient;
|
|
31
|
+
/**
|
|
32
|
+
* Maps an OBO failure to a typed error. Consent/interaction failures need the
|
|
33
|
+
* user (or admin) to act; transient failures are retriable. The raw AADSTS code
|
|
34
|
+
* is surfaced in the message (never token material) so a broken flow is
|
|
35
|
+
* diagnosable — MSAL otherwise swallows 4xx detail.
|
|
36
|
+
*/
|
|
37
|
+
export declare function mapOboError(error: unknown): OutlookMcpError;
|
|
38
|
+
//# sourceMappingURL=obo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"obo.d.ts","sourceRoot":"","sources":["../../../src/remote/auth/obo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAsC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAKjG,oFAAoF;AACpF,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/F,0EAA0E;AAC1E,MAAM,WAAW,SAAS;IACxB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,aAAa,GAAG,IAAI,CAW5F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,gBAAgB,EAAE,UAAU,EAAE,aAAa,GAAG,SAAS,CAgC9F;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAwB3D"}
|
|
@@ -0,0 +1,114 @@
|
|
|
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
|
+
* On-Behalf-Of token exchange (U5). The API app is a confidential client: it
|
|
7
|
+
* exchanges the inbound user assertion (the Entra JWT U4 validated) for a
|
|
8
|
+
* delegated Microsoft Graph token for that same user. Per-user isolation comes
|
|
9
|
+
* from the assertion — MSAL caches each user's OBO result separately, keyed by
|
|
10
|
+
* their identity, so one user's token can never satisfy another's request.
|
|
11
|
+
*
|
|
12
|
+
* The confidential credential (certificate preferred; secret supported for the
|
|
13
|
+
* pilot) lives only in the environment. When neither is configured, OBO fails
|
|
14
|
+
* fast with actionable guidance rather than silently degrading.
|
|
15
|
+
*/
|
|
16
|
+
import { ConfidentialClientApplication } from '@azure/msal-node';
|
|
17
|
+
import { GraphAuthRequiredError, GraphError } from '../../utils/errors.js';
|
|
18
|
+
/** `.default` returns exactly the app's admin-consented Graph scopes. */
|
|
19
|
+
const GRAPH_DEFAULT_SCOPE = ['https://graph.microsoft.com/.default'];
|
|
20
|
+
/**
|
|
21
|
+
* Loads the OBO credential from the environment, preferring a certificate.
|
|
22
|
+
* Returns null when none is configured (OBO disabled — U4 auth still works, but
|
|
23
|
+
* tool calls fail fast until the credential is provisioned in U9).
|
|
24
|
+
*/
|
|
25
|
+
export function loadOboCredential(env = process.env) {
|
|
26
|
+
const thumbprint = env.OUTLOOK_MCP_CONNECTOR_CERT_THUMBPRINT?.trim();
|
|
27
|
+
const privateKey = env.OUTLOOK_MCP_CONNECTOR_CERT_KEY?.trim();
|
|
28
|
+
if (thumbprint != null && thumbprint !== '' && privateKey != null && privateKey !== '') {
|
|
29
|
+
return { kind: 'certificate', thumbprint, privateKey };
|
|
30
|
+
}
|
|
31
|
+
const secret = env.OUTLOOK_MCP_CONNECTOR_CLIENT_SECRET?.trim();
|
|
32
|
+
if (secret != null && secret !== '') {
|
|
33
|
+
return { kind: 'secret', clientSecret: secret };
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Builds the process-wide OBO client (one confidential-client app; per-user
|
|
39
|
+
* isolation is per-assertion, not per-instance).
|
|
40
|
+
*/
|
|
41
|
+
export function createOboClient(config, credential) {
|
|
42
|
+
const authConfig = {
|
|
43
|
+
clientId: config.apiClientId,
|
|
44
|
+
authority: `https://login.microsoftonline.com/${config.tenantId}`,
|
|
45
|
+
...(credential.kind === 'secret'
|
|
46
|
+
? { clientSecret: credential.clientSecret }
|
|
47
|
+
: {
|
|
48
|
+
clientCertificate: {
|
|
49
|
+
thumbprint: credential.thumbprint,
|
|
50
|
+
privateKey: credential.privateKey,
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
const cca = new ConfidentialClientApplication({ auth: authConfig });
|
|
55
|
+
return {
|
|
56
|
+
async acquireGraphToken(userAssertion) {
|
|
57
|
+
let result;
|
|
58
|
+
try {
|
|
59
|
+
result = await cca.acquireTokenOnBehalfOf({
|
|
60
|
+
oboAssertion: userAssertion,
|
|
61
|
+
scopes: GRAPH_DEFAULT_SCOPE,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
throw mapOboError(error);
|
|
66
|
+
}
|
|
67
|
+
if (result?.accessToken == null || result.accessToken === '') {
|
|
68
|
+
throw new GraphAuthRequiredError('session_expired');
|
|
69
|
+
}
|
|
70
|
+
return result.accessToken;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Maps an OBO failure to a typed error. Consent/interaction failures need the
|
|
76
|
+
* user (or admin) to act; transient failures are retriable. The raw AADSTS code
|
|
77
|
+
* is surfaced in the message (never token material) so a broken flow is
|
|
78
|
+
* diagnosable — MSAL otherwise swallows 4xx detail.
|
|
79
|
+
*/
|
|
80
|
+
export function mapOboError(error) {
|
|
81
|
+
// MSAL spreads detail across message / errorCode / errorMessage — search all.
|
|
82
|
+
const message = errorText(error);
|
|
83
|
+
const code = /AADSTS\d+/.exec(message)?.[0];
|
|
84
|
+
// Missing consent for a scope — needs admin consent (see provisioning runbook).
|
|
85
|
+
if (code === 'AADSTS65001') {
|
|
86
|
+
return new GraphError(`On-Behalf-Of failed: consent missing for a Graph scope (${code}). Grant admin ` +
|
|
87
|
+
`consent for the connector API app — see docs/remote/provisioning.md.`);
|
|
88
|
+
}
|
|
89
|
+
// Conditional Access / MFA / password change / revoked — the user must re-auth.
|
|
90
|
+
if (code === 'AADSTS50076' || code === 'AADSTS50079' || /interaction_required|invalid_grant/i.test(message)) {
|
|
91
|
+
return new GraphAuthRequiredError('session_expired');
|
|
92
|
+
}
|
|
93
|
+
// Expired confidential credential — total outage until the cert/secret rotates.
|
|
94
|
+
if (code === 'AADSTS7000222' || code === 'AADSTS7000215') {
|
|
95
|
+
return new GraphError(`On-Behalf-Of failed: the connector's confidential credential is invalid or ` +
|
|
96
|
+
`expired (${code}). Rotate it per docs/remote/provisioning.md.`);
|
|
97
|
+
}
|
|
98
|
+
return new GraphError(`On-Behalf-Of exchange failed${code != null ? ` (${code})` : ''}.`);
|
|
99
|
+
}
|
|
100
|
+
/** Combined error text across MSAL's message / errorCode / errorMessage fields. */
|
|
101
|
+
function errorText(error) {
|
|
102
|
+
const parts = [];
|
|
103
|
+
if (error instanceof Error)
|
|
104
|
+
parts.push(error.message);
|
|
105
|
+
if (error != null && typeof error === 'object') {
|
|
106
|
+
const e = error;
|
|
107
|
+
if (typeof e.errorCode === 'string')
|
|
108
|
+
parts.push(e.errorCode);
|
|
109
|
+
if (typeof e.errorMessage === 'string')
|
|
110
|
+
parts.push(e.errorMessage);
|
|
111
|
+
}
|
|
112
|
+
return parts.length > 0 ? parts.join(' ') : String(error);
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=obo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"obo.js","sourceRoot":"","sources":["../../../src/remote/auth/obo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
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 { jwtVerify } from 'jose';
|
|
12
|
+
import type { RemoteAuthConfig } from '../config.js';
|
|
13
|
+
/** Validated caller identity attached to the request for downstream units. */
|
|
14
|
+
export interface RemoteIdentity {
|
|
15
|
+
/** Entra object id (stable user id). */
|
|
16
|
+
readonly oid: string;
|
|
17
|
+
/** Tenant id. */
|
|
18
|
+
readonly tid: string;
|
|
19
|
+
/** MSAL homeAccountId form (`<oid>.<tid>`) — the per-user state key (U5). */
|
|
20
|
+
readonly homeAccountId: string;
|
|
21
|
+
/** Delegated scopes on the token. */
|
|
22
|
+
readonly scopes: readonly string[];
|
|
23
|
+
/** Expiry (seconds since epoch), when present. */
|
|
24
|
+
readonly expiresAt?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Token failed validation — respond 401 so the client re-authenticates. */
|
|
27
|
+
export declare class AuthChallengeError extends Error {
|
|
28
|
+
readonly reason: string;
|
|
29
|
+
constructor(reason: string, message?: string);
|
|
30
|
+
}
|
|
31
|
+
/** Authenticated but not authorized (guest, app-only, wrong scope, deny-listed) — respond 403. */
|
|
32
|
+
export declare class AuthForbiddenError extends Error {
|
|
33
|
+
readonly reason: string;
|
|
34
|
+
constructor(reason: string, message?: string);
|
|
35
|
+
}
|
|
36
|
+
/** Key source — the tenant JWKS by default; tests inject a local key set. */
|
|
37
|
+
export type KeySource = Parameters<typeof jwtVerify>[1];
|
|
38
|
+
/**
|
|
39
|
+
* Builds a verifier that resolves a bearer token to a {@link RemoteIdentity} or
|
|
40
|
+
* throws {@link AuthChallengeError} (401) / {@link AuthForbiddenError} (403).
|
|
41
|
+
*/
|
|
42
|
+
export declare function createTokenVerifier(config: RemoteAuthConfig, keySource?: KeySource): (token: string) => Promise<RemoteIdentity>;
|
|
43
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/remote/auth/verify.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AAEH,OAAO,EAAsB,SAAS,EAAE,MAAM,MAAM,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKrD,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iBAAiB;IACjB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,kDAAkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,4EAA4E;AAC5E,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBACZ,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAK7C;AAED,kGAAkG;AAClG,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBACZ,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAK7C;AAED,6EAA6E;AAC7E,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AAExD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,gBAAgB,EACxB,SAAS,CAAC,EAAE,SAAS,GACpB,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CA6D5C"}
|