@softeria/ms-365-mcp-server 0.11.4 → 0.12.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,75 +1,65 @@
1
- import logger from '../logger.js';
2
- /**
3
- * Microsoft Bearer Token Auth Middleware validates that the request has a valid Microsoft access token
4
- * The token is passed in the Authorization header as a Bearer token
5
- */
6
- export const microsoftBearerTokenAuthMiddleware = (req, res, next) => {
7
- const authHeader = req.headers.authorization;
8
- if (!authHeader || !authHeader.startsWith('Bearer ')) {
9
- res.status(401).json({ error: 'Missing or invalid access token' });
10
- return;
11
- }
12
- const accessToken = authHeader.substring(7);
13
- // For Microsoft Graph, we don't validate the token here - we'll let the API calls fail if it's invalid
14
- // and handle token refresh in the GraphClient
15
- // Extract refresh token from a custom header (if provided)
16
- const refreshToken = req.headers['x-microsoft-refresh-token'] || '';
17
- // Store tokens in request for later use
18
- req.microsoftAuth = {
19
- accessToken,
20
- refreshToken,
21
- };
22
- next();
1
+ import logger from "../logger.js";
2
+ const microsoftBearerTokenAuthMiddleware = (req, res, next) => {
3
+ const authHeader = req.headers.authorization;
4
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
5
+ res.status(401).json({ error: "Missing or invalid access token" });
6
+ return;
7
+ }
8
+ const accessToken = authHeader.substring(7);
9
+ const refreshToken = req.headers["x-microsoft-refresh-token"] || "";
10
+ req.microsoftAuth = {
11
+ accessToken,
12
+ refreshToken
13
+ };
14
+ next();
23
15
  };
24
- /**
25
- * Exchange authorization code for access token
26
- */
27
- export async function exchangeCodeForToken(code, redirectUri, clientId, clientSecret, tenantId = 'common', codeVerifier) {
28
- const params = new URLSearchParams({
29
- grant_type: 'authorization_code',
30
- code,
31
- redirect_uri: redirectUri,
32
- client_id: clientId,
33
- client_secret: clientSecret,
34
- });
35
- // Add code_verifier for PKCE flow
36
- if (codeVerifier) {
37
- params.append('code_verifier', codeVerifier);
38
- }
39
- const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
40
- method: 'POST',
41
- headers: {
42
- 'Content-Type': 'application/x-www-form-urlencoded',
43
- },
44
- body: params
45
- });
46
- if (!response.ok) {
47
- const error = await response.text();
48
- logger.error(`Failed to exchange code for token: ${error}`);
49
- throw new Error(`Failed to exchange code for token: ${error}`);
50
- }
51
- return response.json();
16
+ async function exchangeCodeForToken(code, redirectUri, clientId, clientSecret, tenantId = "common", codeVerifier) {
17
+ const params = new URLSearchParams({
18
+ grant_type: "authorization_code",
19
+ code,
20
+ redirect_uri: redirectUri,
21
+ client_id: clientId,
22
+ client_secret: clientSecret
23
+ });
24
+ if (codeVerifier) {
25
+ params.append("code_verifier", codeVerifier);
26
+ }
27
+ const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
28
+ method: "POST",
29
+ headers: {
30
+ "Content-Type": "application/x-www-form-urlencoded"
31
+ },
32
+ body: params
33
+ });
34
+ if (!response.ok) {
35
+ const error = await response.text();
36
+ logger.error(`Failed to exchange code for token: ${error}`);
37
+ throw new Error(`Failed to exchange code for token: ${error}`);
38
+ }
39
+ return response.json();
52
40
  }
53
- /**
54
- * Refresh an access token
55
- */
56
- export async function refreshAccessToken(refreshToken, clientId, clientSecret, tenantId = 'common') {
57
- const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
58
- method: 'POST',
59
- headers: {
60
- 'Content-Type': 'application/x-www-form-urlencoded',
61
- },
62
- body: new URLSearchParams({
63
- grant_type: 'refresh_token',
64
- refresh_token: refreshToken,
65
- client_id: clientId,
66
- client_secret: clientSecret,
67
- })
68
- });
69
- if (!response.ok) {
70
- const error = await response.text();
71
- logger.error(`Failed to refresh token: ${error}`);
72
- throw new Error(`Failed to refresh token: ${error}`);
73
- }
74
- return response.json();
41
+ async function refreshAccessToken(refreshToken, clientId, clientSecret, tenantId = "common") {
42
+ const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
43
+ method: "POST",
44
+ headers: {
45
+ "Content-Type": "application/x-www-form-urlencoded"
46
+ },
47
+ body: new URLSearchParams({
48
+ grant_type: "refresh_token",
49
+ refresh_token: refreshToken,
50
+ client_id: clientId,
51
+ client_secret: clientSecret
52
+ })
53
+ });
54
+ if (!response.ok) {
55
+ const error = await response.text();
56
+ logger.error(`Failed to refresh token: ${error}`);
57
+ throw new Error(`Failed to refresh token: ${error}`);
58
+ }
59
+ return response.json();
75
60
  }
61
+ export {
62
+ exchangeCodeForToken,
63
+ microsoftBearerTokenAuthMiddleware,
64
+ refreshAccessToken
65
+ };
package/dist/logger.js CHANGED
@@ -1,33 +1,42 @@
1
- import winston from 'winston';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import fs from 'fs';
1
+ import winston from "winston";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import fs from "fs";
5
5
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
- const logsDir = path.join(__dirname, '..', 'logs');
6
+ const logsDir = path.join(__dirname, "..", "logs");
7
7
  if (!fs.existsSync(logsDir)) {
8
- fs.mkdirSync(logsDir);
8
+ fs.mkdirSync(logsDir);
9
9
  }
10
10
  const logger = winston.createLogger({
11
- level: process.env.LOG_LEVEL || 'info',
12
- format: winston.format.combine(winston.format.timestamp({
13
- format: 'YYYY-MM-DD HH:mm:ss',
14
- }), winston.format.printf(({ level, message, timestamp }) => {
15
- return `${timestamp} ${level.toUpperCase()}: ${message}`;
16
- })),
17
- transports: [
18
- new winston.transports.File({
19
- filename: path.join(logsDir, 'error.log'),
20
- level: 'error',
21
- }),
22
- new winston.transports.File({
23
- filename: path.join(logsDir, 'mcp-server.log'),
24
- }),
25
- ],
11
+ level: process.env.LOG_LEVEL || "info",
12
+ format: winston.format.combine(
13
+ winston.format.timestamp({
14
+ format: "YYYY-MM-DD HH:mm:ss"
15
+ }),
16
+ winston.format.printf(({ level, message, timestamp }) => {
17
+ return `${timestamp} ${level.toUpperCase()}: ${message}`;
18
+ })
19
+ ),
20
+ transports: [
21
+ new winston.transports.File({
22
+ filename: path.join(logsDir, "error.log"),
23
+ level: "error"
24
+ }),
25
+ new winston.transports.File({
26
+ filename: path.join(logsDir, "mcp-server.log")
27
+ })
28
+ ]
26
29
  });
27
- export const enableConsoleLogging = () => {
28
- logger.add(new winston.transports.Console({
29
- format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
30
- silent: process.env.SILENT === 'true' || process.env.SILENT === '1',
31
- }));
30
+ const enableConsoleLogging = () => {
31
+ logger.add(
32
+ new winston.transports.Console({
33
+ format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
34
+ silent: process.env.SILENT === "true" || process.env.SILENT === "1"
35
+ })
36
+ );
37
+ };
38
+ var logger_default = logger;
39
+ export {
40
+ logger_default as default,
41
+ enableConsoleLogging
32
42
  };
33
- export default logger;
@@ -1,48 +1,49 @@
1
- import { ProxyOAuthServerProvider } from '@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js';
2
- import logger from './logger.js';
3
- export class MicrosoftOAuthProvider extends ProxyOAuthServerProvider {
4
- constructor(authManager) {
5
- const tenantId = process.env.MS365_MCP_TENANT_ID || 'common';
6
- const clientId = process.env.MS365_MCP_CLIENT_ID || '084a3e9f-a9f4-43f7-89f9-d229cf97853e';
7
- super({
8
- endpoints: {
9
- authorizationUrl: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`,
10
- tokenUrl: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
11
- revocationUrl: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/logout`,
12
- },
13
- verifyAccessToken: async (token) => {
14
- try {
15
- const response = await fetch('https://graph.microsoft.com/v1.0/me', {
16
- headers: {
17
- Authorization: `Bearer ${token}`,
18
- },
19
- });
20
- if (response.ok) {
21
- const userData = await response.json();
22
- logger.info(`OAuth token verified for user: ${userData.userPrincipalName}`);
23
- await authManager.setOAuthToken(token);
24
- return {
25
- token,
26
- clientId,
27
- scopes: [],
28
- };
29
- }
30
- else {
31
- throw new Error(`Token verification failed: ${response.status}`);
32
- }
33
- }
34
- catch (error) {
35
- logger.error(`OAuth token verification error: ${error}`);
36
- throw error;
37
- }
38
- },
39
- getClient: async (client_id) => {
40
- return {
41
- client_id,
42
- redirect_uris: ['http://localhost:3000/callback'],
43
- };
44
- },
45
- });
46
- this.authManager = authManager;
47
- }
1
+ import { ProxyOAuthServerProvider } from "@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js";
2
+ import logger from "./logger.js";
3
+ class MicrosoftOAuthProvider extends ProxyOAuthServerProvider {
4
+ constructor(authManager) {
5
+ const tenantId = process.env.MS365_MCP_TENANT_ID || "common";
6
+ const clientId = process.env.MS365_MCP_CLIENT_ID || "084a3e9f-a9f4-43f7-89f9-d229cf97853e";
7
+ super({
8
+ endpoints: {
9
+ authorizationUrl: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`,
10
+ tokenUrl: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
11
+ revocationUrl: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/logout`
12
+ },
13
+ verifyAccessToken: async (token) => {
14
+ try {
15
+ const response = await fetch("https://graph.microsoft.com/v1.0/me", {
16
+ headers: {
17
+ Authorization: `Bearer ${token}`
18
+ }
19
+ });
20
+ if (response.ok) {
21
+ const userData = await response.json();
22
+ logger.info(`OAuth token verified for user: ${userData.userPrincipalName}`);
23
+ await authManager.setOAuthToken(token);
24
+ return {
25
+ token,
26
+ clientId,
27
+ scopes: []
28
+ };
29
+ } else {
30
+ throw new Error(`Token verification failed: ${response.status}`);
31
+ }
32
+ } catch (error) {
33
+ logger.error(`OAuth token verification error: ${error}`);
34
+ throw error;
35
+ }
36
+ },
37
+ getClient: async (client_id) => {
38
+ return {
39
+ client_id,
40
+ redirect_uris: ["http://localhost:3000/callback"]
41
+ };
42
+ }
43
+ });
44
+ this.authManager = authManager;
45
+ }
48
46
  }
47
+ export {
48
+ MicrosoftOAuthProvider
49
+ };