@softeria/ms-365-mcp-server 0.107.0 → 0.107.1

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.
@@ -0,0 +1,33 @@
1
+ const LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
2
+ function isLoopbackHost(host) {
3
+ return LOOPBACK_HOSTS.has(host.toLowerCase());
4
+ }
5
+ function parseAllowlist(raw) {
6
+ if (!raw) return null;
7
+ const list = raw.split(",").map((s) => s.trim()).filter(Boolean);
8
+ return list.length > 0 ? list : null;
9
+ }
10
+ function isAllowedRedirectUri(value, allowlist) {
11
+ if (typeof value !== "string" || value.length === 0) return false;
12
+ let url;
13
+ try {
14
+ url = new URL(value);
15
+ } catch {
16
+ return false;
17
+ }
18
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
19
+ return false;
20
+ }
21
+ if (allowlist && allowlist.length > 0) {
22
+ return allowlist.includes(value);
23
+ }
24
+ if (url.protocol === "http:") {
25
+ return isLoopbackHost(url.hostname);
26
+ }
27
+ return true;
28
+ }
29
+ export {
30
+ isAllowedRedirectUri,
31
+ isLoopbackHost,
32
+ parseAllowlist
33
+ };
package/dist/server.js CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  microsoftBearerTokenAuthMiddleware,
16
16
  refreshAccessToken
17
17
  } from "./lib/microsoft-auth.js";
18
+ import { isAllowedRedirectUri, parseAllowlist } from "./lib/redirect-uri-validation.js";
18
19
  import { getSecrets } from "./secrets.js";
19
20
  import { getCloudEndpoints } from "./cloud-config.js";
20
21
  import { requestContext } from "./request-context.js";
@@ -226,6 +227,20 @@ class MicrosoftGraphServer {
226
227
  const clientCodeChallenge = url.searchParams.get("code_challenge");
227
228
  const clientCodeChallengeMethod = url.searchParams.get("code_challenge_method");
228
229
  const state = url.searchParams.get("state");
230
+ const redirectUriParam = url.searchParams.get("redirect_uri");
231
+ if (redirectUriParam) {
232
+ const allowlist = parseAllowlist(process.env.MS365_MCP_ALLOWED_REDIRECT_URIS);
233
+ if (!isAllowedRedirectUri(redirectUriParam, allowlist)) {
234
+ logger.warn("Rejected /authorize request with disallowed redirect_uri", {
235
+ redirect_uri: redirectUriParam
236
+ });
237
+ res.status(400).json({
238
+ error: "invalid_request",
239
+ error_description: "redirect_uri is not allowed"
240
+ });
241
+ return;
242
+ }
243
+ }
229
244
  const allowedParams = [
230
245
  "response_type",
231
246
  "redirect_uri",
@@ -130,6 +130,28 @@ When deploying for an organization, create a dedicated app registration instead
130
130
 
131
131
  5. **Store credentials** in Key Vault (see [Azure Key Vault Integration](../README.md#azure-key-vault-integration))
132
132
 
133
+ ## Redirect URI Validation
134
+
135
+ The /authorize endpoint defensively validates client-supplied `redirect_uri` values before forwarding them to Microsoft Entra (CWE-601, Open Redirect). Microsoft Entra also validates the URI against your app registration, but this server-side check rejects obviously dangerous schemes (`javascript:`, `data:`, `file:`, …) and arbitrary remote `http://` origins before the request leaves the server.
136
+
137
+ Default behaviour (no explicit allowlist):
138
+
139
+ - Only `http:` and `https:` schemes are accepted.
140
+ - `http:` is only allowed for loopback hosts (`localhost`, `127.0.0.1`, `::1`).
141
+ - All other `https://` origins are accepted (Entra still has the final say).
142
+
143
+ For production deployments, configure an explicit allowlist via the `MS365_MCP_ALLOWED_REDIRECT_URIS` environment variable. It takes a comma-separated list of exact URIs; only exact string matches pass validation:
144
+
145
+ ```bash
146
+ # Single redirect URI
147
+ MS365_MCP_ALLOWED_REDIRECT_URIS=https://mcp.example.com/auth/callback
148
+
149
+ # Multiple URIs (comma-separated, no spaces required)
150
+ MS365_MCP_ALLOWED_REDIRECT_URIS=https://mcp.example.com/auth/callback,https://staging.example.com/auth/callback
151
+ ```
152
+
153
+ The list should mirror the redirect URIs registered on your Azure AD app registration. Leaving the variable unset falls back to the default behaviour above, which is appropriate for local development but not recommended for shared/production deployments.
154
+
133
155
  ## Reverse Proxy / Custom Domain
134
156
 
135
157
  When running behind a reverse proxy, set `MS365_MCP_PUBLIC_URL` so that the OAuth authorize URL handed back to the user's browser is resolvable from outside the server's network:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.107.0",
3
+ "version": "0.107.1",
4
4
  "description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",