VPN环境下Node.js用Microsoft Graph Client遇TypeError: fetch failed
VPN环境下调用Microsoft Graph API出现TypeError: fetch failed的排查与解决
问题说明
- 使用环境:Node.js 20.9,依赖
msal-node、@microsoft-graph-client - 异常现象:
- VPN外运行代码完全正常
- VPN内
msal-node能成功获取访问令牌,但调用Microsoft Graph API时始终抛出TypeError: fetch failed错误
原代码
const dotenv = require("dotenv"); dotenv.config(); let groupid=""; let driveitemid=""; var msal = require('@azure/msal-node'); MicrosoftGraph = require("@microsoft/microsoft-graph-client"); const HttpsProxyAgent = require('https-proxy-agent'); const fetch = require('node-fetch-with-proxy'); const networkClient={ sendGetRequestAsync:async function (url, options) { const response = await fetch(url, options); const json = await response.json(); const headers = response.headers.raw(); return { headers: Object.create(Object.prototype, headers), body: json, status: response.status } }, sendPostRequestAsync:async function (url, options) { const sendingOptions = options || {}; sendingOptions.method = 'post'; const response = await fetch(url, sendingOptions); const json = await response.json() const headers = response.headers.raw(); return { headers: Object.create(Object.prototype, headers), body: json, status: response.status, agent:sendingOptions.agent } }} const agent = new HttpsProxyAgent.HttpsProxyAgent(process.env.http_proxy); //require("isomorphic-fetch"); const config = require(`./config/AAD.json`); async function getClientCredentialsToken(cca) { const clientCredentialRequest = { scopes: ["https://graph.microsoft.com/.default"], skipCache: true, }; return await cca .acquireTokenByClientCredential(clientCredentialRequest) .then((response) => { console.log("Response: ", response); return response; }).catch((error) => { console.log(error); }); } async function callGraph(accessToken) { console.log(agent); var client =await MicrosoftGraph.Client.init({ debugLogging: false, authProvider: function(authDone) { authDone(null, accessToken); }, fetchOptions: { //agent: prxyagent }, }); let url=`https://graph.microsoft.com/v1.0/groups/${groupid}/drive/items/${driveitemid}?$expand=thumbnails` try { result=await client.api(url).get(); console.log(result); } catch (error) { console.log(error); } } let main=async()=>{ const clientConfig = { auth: config.authOptions, system: { networkClient: networkClient } }; const confidentialClientApplication = new msal.ConfidentialClientApplication(clientConfig); let tokenresponse=await getClientCredentialsToken(confidentialClientApplication); if(tokenresponse.accessToken) { callGraph(tokenresponse.accessToken); } }; main();
报错信息
TypeError: fetch failed at Object.fetch (node:internal/deps/undici/undici:11372:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { statusCode: -1, code: 'TypeError',
原因分析
问题出在代理配置的覆盖范围:
msal-node通过自定义networkClient使用了带代理支持的node-fetch-with-proxy,所以能正常穿透VPN获取令牌- 但
@microsoft-graph-client初始化时没有配置代理,默认使用Node.js 20内置的undici fetch,该请求未走VPN代理,导致连接失败
解决方法
修改callGraph函数中Graph Client的初始化配置,将代理agent传入fetchOptions,同时指定使用带代理支持的fetch工具:
async function callGraph(accessToken) { console.log(agent); var client = await MicrosoftGraph.Client.init({ debugLogging: false, authProvider: function(authDone) { authDone(null, accessToken); }, fetchOptions: { agent: agent // 启用代理配置 }, fetch: fetch // 指定使用带代理支持的node-fetch-with-proxy }); let url=`https://graph.microsoft.com/v1.0/groups/${groupid}/drive/items/${driveitemid}?$expand=thumbnails` try { result=await client.api(url).get(); console.log(result); } catch (error) { console.log(error); } }
额外检查项
- 确认
process.env.http_proxy环境变量已正确设置为VPN代理地址(格式如http://proxy-host:port) - 若代理需要认证,可在代理地址中加入用户名密码:
http://username:password@proxy-host:port
内容的提问来源于stack exchange,提问作者akanandhari
相关产品推荐
相关产品推荐

