@softeria/ms-365-mcp-server 0.82.0 → 0.83.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.
@@ -1,5 +1,4 @@
1
1
  import logger from "./logger.js";
2
- import { refreshAccessToken } from "./lib/microsoft-auth.js";
3
2
  import { encode as toonEncode } from "@toon-format/toon";
4
3
  import { getCloudEndpoints } from "./cloud-config.js";
5
4
  import { getRequestTokens } from "./request-context.js";
@@ -33,18 +32,12 @@ class GraphClient {
33
32
  }
34
33
  async makeRequest(endpoint, options = {}) {
35
34
  const contextTokens = getRequestTokens();
36
- let accessToken = options.accessToken ?? contextTokens?.accessToken ?? await this.authManager.getToken();
37
- const refreshToken = options.refreshToken ?? contextTokens?.refreshToken;
35
+ const accessToken = options.accessToken ?? contextTokens?.accessToken ?? await this.authManager.getToken();
38
36
  if (!accessToken) {
39
37
  throw new Error("No access token available");
40
38
  }
41
39
  try {
42
- let response = await this.performRequest(endpoint, accessToken, options);
43
- if (response.status === 401 && refreshToken) {
44
- const newTokens = await this.refreshAccessToken(refreshToken);
45
- accessToken = newTokens.accessToken;
46
- response = await this.performRequest(endpoint, accessToken, options);
47
- }
40
+ const response = await this.performRequest(endpoint, accessToken, options);
48
41
  if (response.status === 403) {
49
42
  const errorText = await response.text();
50
43
  if (errorText.includes("scope") || errorText.includes("permission")) {
@@ -100,27 +93,6 @@ class GraphClient {
100
93
  throw error;
101
94
  }
102
95
  }
103
- async refreshAccessToken(refreshToken) {
104
- const tenantId = this.secrets.tenantId || "common";
105
- const clientId = this.secrets.clientId;
106
- const clientSecret = this.secrets.clientSecret;
107
- if (clientSecret) {
108
- logger.info("GraphClient: Refreshing token with confidential client");
109
- } else {
110
- logger.info("GraphClient: Refreshing token with public client");
111
- }
112
- const response = await refreshAccessToken(
113
- refreshToken,
114
- clientId,
115
- clientSecret,
116
- tenantId,
117
- this.secrets.cloudType
118
- );
119
- return {
120
- accessToken: response.access_token,
121
- refreshToken: response.refresh_token
122
- };
123
- }
124
96
  async performRequest(endpoint, accessToken, options) {
125
97
  const cloudEndpoints = getCloudEndpoints(this.secrets.cloudType);
126
98
  const url = `${cloudEndpoints.graphApi}/v1.0${endpoint}`;
@@ -1,17 +1,43 @@
1
1
  import logger from "../logger.js";
2
2
  import { getCloudEndpoints } from "../cloud-config.js";
3
+ function buildWwwAuthenticate(req, error, description) {
4
+ const protocol = req.secure ? "https" : "http";
5
+ const origin = `${protocol}://${req.get("host")}`;
6
+ const resourceMetadata = `${origin}/.well-known/oauth-protected-resource`;
7
+ return `Bearer resource_metadata="${resourceMetadata}", error="${error}", error_description="${description}"`;
8
+ }
9
+ function isJwtExpired(token) {
10
+ const parts = token.split(".");
11
+ if (parts.length !== 3) return false;
12
+ try {
13
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
14
+ if (typeof payload.exp !== "number") return false;
15
+ return payload.exp * 1e3 < Date.now();
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
3
20
  const microsoftBearerTokenAuthMiddleware = (req, res, next) => {
4
21
  const authHeader = req.headers.authorization;
5
22
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
6
- res.status(401).json({ error: "Missing or invalid access token" });
23
+ res.status(401).set(
24
+ "WWW-Authenticate",
25
+ buildWwwAuthenticate(req, "invalid_token", "Missing or malformed Authorization header")
26
+ ).json({
27
+ error: "invalid_token",
28
+ error_description: "Missing or malformed Authorization header"
29
+ });
7
30
  return;
8
31
  }
9
32
  const accessToken = authHeader.substring(7);
10
- const refreshToken = req.headers["x-microsoft-refresh-token"] || "";
11
- req.microsoftAuth = {
12
- accessToken,
13
- refreshToken
14
- };
33
+ if (isJwtExpired(accessToken)) {
34
+ res.status(401).set(
35
+ "WWW-Authenticate",
36
+ buildWwwAuthenticate(req, "invalid_token", "The access token has expired")
37
+ ).json({ error: "invalid_token", error_description: "The access token has expired" });
38
+ return;
39
+ }
40
+ req.microsoftAuth = { accessToken };
15
41
  next();
16
42
  };
17
43
  async function exchangeCodeForToken(code, redirectUri, clientId, clientSecret, tenantId = "common", codeVerifier, cloudType = "global") {
package/dist/server.js CHANGED
@@ -388,13 +388,7 @@ class MicrosoftGraphServer {
388
388
  };
389
389
  try {
390
390
  if (req.microsoftAuth) {
391
- await requestContext.run(
392
- {
393
- accessToken: req.microsoftAuth.accessToken,
394
- refreshToken: req.microsoftAuth.refreshToken
395
- },
396
- handler
397
- );
391
+ await requestContext.run({ accessToken: req.microsoftAuth.accessToken }, handler);
398
392
  } else {
399
393
  await handler();
400
394
  }
@@ -432,13 +426,7 @@ class MicrosoftGraphServer {
432
426
  };
433
427
  try {
434
428
  if (req.microsoftAuth) {
435
- await requestContext.run(
436
- {
437
- accessToken: req.microsoftAuth.accessToken,
438
- refreshToken: req.microsoftAuth.refreshToken
439
- },
440
- handler
441
- );
429
+ await requestContext.run({ accessToken: req.microsoftAuth.accessToken }, handler);
442
430
  } else {
443
431
  await handler();
444
432
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.82.0",
3
+ "version": "0.83.0",
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",