You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Node.js结合Azure AD 2.0 Passport策略读取OneDrive文件?

Node.js 通过 Passport Azure AD 2.0 读取 OneDrive 文件完整方案

我来给你一套亲测可行的方案,从认证到读取文件一步到位,帮你避开官方文档里没说清的细节:

一、Azure AD 应用前置配置

首先得在Azure Portal里搞定应用注册,这一步错了后面全白搭:

  • 注册一个Web应用(适配Node.js后端场景)
  • 设置重定向URI:比如http://localhost:3000/auth/callback(本地测试用)
  • 记录下客户端ID(CLIENT_ID)、客户端密码(CLIENT_SECRET)、租户ID(TENANT_ID)
  • 前往「API权限」,添加Microsoft Graph的权限:
    • 委派权限:Files.Read(读取当前用户的文件)或Files.Read.All(读取组织内所有文件,需管理员同意)
    • 别忘了点击「授予管理员同意」(个人账号登录后会自动授权,租户应用需要管理员操作)

二、项目初始化与依赖安装

先创建项目并安装必备依赖:

mkdir onedrive-node && cd onedrive-node
npm init -y
npm install express passport passport-azure-ad axios dotenv express-session

三、Passport Azure AD 认证配置

  1. 创建.env文件存储敏感信息:
CLIENT_ID=你的客户端ID
CLIENT_SECRET=你的客户端密码
TENANT_ID=你的租户ID
REDIRECT_URI=http://localhost:3000/auth/callback
SESSION_SECRET=随便写一个字符串(比如abc123xyz)
PORT=3000
  1. 编写核心服务文件app.js:
require('dotenv').config();
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const { OIDCStrategy } = require('passport-azure-ad');
const axios = require('axios');

const app = express();

// 配置Session,用于存储用户认证状态和Token
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: { secure: false } // 本地测试用false,生产环境必须开true(HTTPS环境)
}));

// 初始化Passport
app.use(passport.initialize());
app.use(passport.session());

// 序列化/反序列化用户(将用户信息存入Session)
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));

// 配置Azure AD OIDC认证策略
passport.use(new OIDCStrategy({
  identityMetadata: `https://login.microsoftonline.com/${process.env.TENANT_ID}/v2.0/.well-known/openid-configuration`,
  clientID: process.env.CLIENT_ID,
  clientSecret: process.env.CLIENT_SECRET,
  responseType: 'code id_token',
  responseMode: 'form_post',
  redirectUrl: process.env.REDIRECT_URI,
  allowHttpForRedirectUrl: true,
  scope: ['openid', 'profile', 'email', 'Files.Read', 'offline_access'], // offline_access用于获取刷新Token
  loggingLevel: 'info',
  passReqToCallback: false
}, (iss, sub, profile, accessToken, refreshToken, done) => {
  // 将Token附加到用户对象,方便后续调用Graph API
  profile.accessToken = accessToken;
  profile.refreshToken = refreshToken;
  return done(null, profile);
}));

// 认证路由:触发Azure AD登录
app.get('/login', passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }), (req, res) => {
  res.redirect('/');
});

// 回调路由:处理Azure AD认证后的跳转
app.get('/auth/callback', passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }), (req, res) => {
  res.redirect('/read-onedrive-file');
});

// 退出登录路由
app.get('/logout', (req, res) => {
  req.logout(() => res.redirect('/'));
});

// 核心功能:读取OneDrive文件
app.get('/read-onedrive-file', async (req, res) => {
  if (!req.isAuthenticated()) {
    return res.redirect('/login');
  }

  try {
    // 1. 获取OneDrive根目录下的文件列表
    const filesResponse = await axios.get('https://graph.microsoft.com/v1.0/me/drive/root/children', {
      headers: { Authorization: `Bearer ${req.user.accessToken}` }
    });

    const firstFile = filesResponse.data.value[0];
    if (!firstFile) {
      return res.send('OneDrive根目录下没有文件哦');
    }

    // 2. 获取目标文件内容(小文件用text模式,大文件建议用流式下载)
    const fileContentResponse = await axios.get(`https://graph.microsoft.com/v1.0/me/drive/items/${firstFile.id}/content`, {
      headers: { Authorization: `Bearer ${req.user.accessToken}` },
      responseType: 'text' // 二进制文件可改为'arraybuffer'
    });

    res.send(`
      <h3>找到文件:${firstFile.name}</h3>
      <h4>文件内容:</h4>
      <pre>${fileContentResponse.data}</pre>
    `);
  } catch (error) {
    // Token过期处理:用Refresh Token获取新的Access Token
    if (error.response?.status === 401) {
      const tokenResponse = await axios.post(`https://login.microsoftonline.com/${process.env.TENANT_ID}/oauth2/v2.0/token`, new URLSearchParams({
        client_id: process.env.CLIENT_ID,
        client_secret: process.env.CLIENT_SECRET,
        grant_type: 'refresh_token',
        refresh_token: req.user.refreshToken,
        scope: 'Files.Read offline_access'
      }));

      // 更新Session中的Token
      req.user.accessToken = tokenResponse.data.access_token;
      req.user.refreshToken = tokenResponse.data.refresh_token;
      return res.redirect('/read-onedrive-file');
    }
    res.send(`出错了:${error.message}`);
  }
});

// 首页
app.get('/', (req, res) => {
  if (req.isAuthenticated()) {
    res.send(`
      <h1>已登录成功</h1>
      <a href="/read-onedrive-file">读取OneDrive文件</a> | <a href="/logout">退出登录</a>
    `);
  } else {
    res.send('<a href="/login">点击登录Azure AD</a>');
  }
});

app.listen(process.env.PORT, () => {
  console.log(`服务运行在http://localhost:${process.env.PORT}`);
});

四、运行测试

  1. 启动服务:node app.js
  2. 访问http://localhost:3000,点击登录并完成微软账号授权
  3. 授权成功后自动跳转到文件读取页面,即可看到OneDrive中的文件内容

五、简化方案:直接使用微软Graph SDK

如果你觉得Passport配置繁琐,可以用官方Graph SDK,代码更简洁:

npm install @microsoft/microsoft-graph-client @azure/msal-node

示例代码(后台服务模式,无需用户交互):

require('dotenv').config();
const { Client } = require('@microsoft/microsoft-graph-client');
const { ClientCredentialAuthenticationProvider } = require('@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials');
const { ClientSecretCredential } = require('@azure/identity');

// 客户端凭证模式(适合后台服务,需应用级权限)
const credential = new ClientSecretCredential(
  process.env.TENANT_ID,
  process.env.CLIENT_ID,
  process.env.CLIENT_SECRET
);

const authProvider = new ClientCredentialAuthenticationProvider(credential, {
  scopes: ['https://graph.microsoft.com/.default']
});

const client = Client.initWithMiddleware({ authProvider });

// 读取指定用户的OneDrive文件(需Files.Read.All权限)
async function getOneDriveFiles(userId) {
  try {
    const files = await client.api(`/users/${userId}/drive/root/children`).get();
    console.log('文件列表:', files.value);
    
    const firstFile = files.value[0];
    const fileContent = await client.api(`/users/${userId}/drive/items/${firstFile.id}/content`).get();
    console.log('文件内容:', fileContent);
  } catch (error) {
    console.error('出错了:', error);
  }
}

// 替换为目标用户的ID或邮箱
getOneDriveFiles('user@yourdomain.com');

常见坑点提醒

  • 必须添加offline_access scope,否则无法获取Refresh Token,Access Token过期后会无法继续调用API
  • 权限类型要匹配:用户交互场景用委派权限,后台服务场景用应用权限
  • 大文件建议使用流式下载,避免内存溢出
  • 本地测试时,Azure Portal中的重定向URI要和代码中完全一致(包括http/https)

内容的提问来源于stack exchange,提问作者dawood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:51:37