feishu-user-token-mcp-v2
Version:
A Model Context Protocol (MCP) server for Feishu user token management and authentication
144 lines (143 loc) • 5.66 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAuthorizationCode = getAuthorizationCode;
exports.refreshAccessToken = refreshAccessToken;
exports.getAccessToken = getAccessToken;
const express_1 = __importDefault(require("express"));
const open_1 = __importDefault(require("open"));
const axios_1 = __importDefault(require("axios"));
const feishuAxios = axios_1.default.create();
async function getAuthorizationCode(app_id, redirectUri, timeoutMs = 300000 // 5 minutes default timeout
) {
const url = new URL(redirectUri);
const port = parseInt(url.port);
const callbackPath = url.pathname;
return new Promise((resolve, reject) => {
const app = (0, express_1.default)();
let server = null;
let timeoutId = null;
let resolved = false;
const cleanup = () => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
if (server) {
try {
server.close();
}
catch (err) {
console.warn("Error closing server during cleanup:", err);
}
server = null;
}
};
const safeResolve = (value) => {
if (!resolved) {
resolved = true;
cleanup();
resolve(value);
}
};
const safeReject = (error) => {
if (!resolved) {
resolved = true;
cleanup();
reject(error);
}
};
// Set timeout
timeoutId = setTimeout(() => {
safeReject(new Error(`Authorization timeout after ${timeoutMs}ms. Please try again.`));
}, timeoutMs);
try {
server = app.listen(port, () => {
const authUrl = `https://open.feishu.cn/open-apis/authen/v1/index?app_id=${app_id}&redirect_uri=${encodeURIComponent(redirectUri)}`;
console.log("Opening browser for authorization", authUrl);
console.log(`Waiting for authorization (timeout: ${timeoutMs / 1000}s)...`);
(0, open_1.default)(authUrl).catch((err) => console.error("Failed to open browser automatically:", err));
});
app.get(callbackPath, (req, res) => {
try {
const code = req.query.code;
if (code) {
res.send("Authorization successful! You can close this window.");
safeResolve(code);
}
else {
const error = "Authorization failed: no code received.";
res.status(400).send(error);
safeReject(new Error(error));
}
}
catch (err) {
res.status(500).send("Internal server error");
safeReject(new Error(`Callback error: ${err}`));
}
});
server.on("error", (err) => {
safeReject(new Error(`Server error: ${err.message}`));
});
}
catch (err) {
safeReject(new Error(`Failed to start authorization server: ${err}`));
}
});
}
async function createAccessToken(code, { app_id, app_secret }, redirectUri) {
const response = await feishuAxios.post("https://open.feishu.cn/open-apis/authen/v2/oauth/token", {
grant_type: "authorization_code",
client_id: app_id,
client_secret: app_secret,
code: code,
redirect_uri: redirectUri,
});
if (response.data.code !== 0) {
console.error("Failed to create access token", response.data);
throw new Error(`Failed to create access token: ${response.data.msg} (code: ${response.data.code})`);
}
const { access_token, refresh_token } = response.data;
console.log("Access token refreshed successfully.");
return {
access_token,
refresh_token,
};
}
async function refreshAccessToken(refreshToken) {
const response = await feishuAxios.post("https://open.feishu.cn/open-apis/authen/v2/oauth/token", {
grant_type: "refresh_token",
refresh_token: refreshToken,
}, {
headers: {
"Content-Type": "application/json; charset=utf-8",
},
});
if (response.data.code !== 0) {
throw new Error(`Failed to refresh access token: ${response.data.msg}`);
}
const { access_token, refresh_token } = response.data;
console.log("Access token refreshed successfully.");
return {
access_token,
refresh_token,
};
}
async function getAccessToken({ app_id, app_secret, user_token, refresh_token }, redirectUri) {
// 如果提供了refresh_token参数,尝试刷新
if (refresh_token) {
try {
console.log("Attempting to refresh access token...");
return await refreshAccessToken(refresh_token);
}
catch (error) {
console.warn("Failed to refresh token, will create a new one.", error.message);
}
}
// 如果没有refresh_token或刷新失败,走授权码流程获取新token
console.log("Creating new access token via authorization code...");
const code = await getAuthorizationCode(app_id, redirectUri);
return await createAccessToken(code, { app_id, app_secret }, redirectUri);
}