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

基于MSAL的msgraph-training-reactspa React SPA获取刷新令牌并用于CRON任务调用Microsoft Graph API的实现方法咨询

Great question! Let's walk through how to make this work—since by default, SPAs using MSAL (like the msgraph-training-reactspa SDK) don't get refresh tokens because of Azure AD's security defaults for public clients. Here's what you need to do step by step:

1. Update Your Azure AD App Registration

First, you need to configure your app registration to allow refresh tokens for your SPA:

  • Log into the Azure Portal, navigate to your app registration.
  • Go to Authentication > Under "Advanced settings", set Allow public client flows to Yes. This enables Azure AD to issue refresh tokens to your SPA (a public client).
  • Ensure you've added your SPA's redirect URI under the "Single-page application" platform section.

2. Modify Your React SPA's MSAL Configuration

Next, update your MSAL setup to request the offline_access scope (required to get a refresh token) and extract the refresh token to send to your backend:

Initialize MSAL with the Right Scopes

import { PublicClientApplication } from '@azure/msal-browser';

// MSAL configuration
const msalConfig = {
  auth: {
    clientId: "YOUR_CLIENT_ID",
    authority: "https://login.microsoftonline.com/YOUR_TENANT_ID",
    redirectUri: window.location.origin, // Match your Azure AD redirect URI
  },
  cache: {
    cacheLocation: "sessionStorage", // Avoid localStorage for better security
    storeAuthStateInCookie: false,
  }
};

const msalInstance = new PublicClientApplication(msalConfig);

// Login request with offline_access scope
const loginRequest = {
  scopes: ["user.read", "mail.read", "offline_access"] // Include offline_access here
};

Handle Login and Extract the Refresh Token

After the user logs in, you can pull the refresh token from MSAL's token cache and send it to your backend for storage:

async function handleLogin() {
  try {
    const loginResponse = await msalInstance.loginPopup(loginRequest);
    const account = msalInstance.getAccountByUsername(loginResponse.account.username);
    
    // Get the refresh token from MSAL's cache
    const tokenCache = msalInstance.getTokenCache();
    const allRefreshTokens = await tokenCache.getAllRefreshTokens();
    const userRefreshToken = allRefreshTokens.find(rt => rt.account.homeAccountId === account.homeAccountId);

    if (userRefreshToken) {
      // Send refresh token to your backend API for database storage
      await fetch("/api/save-refresh-token", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          refreshToken: userRefreshToken.secret,
          userId: account.localAccountId
        })
      });
    }
  } catch (err) {
    console.error("Login failed:", err);
  }
}

Important: Never store the refresh token in the frontend long-term—send it to your backend immediately, as frontend storage is insecure for sensitive tokens.

3. Use the Refresh Token in Your Backend Cron Job

For your cron task, you'll need to use MSAL for Node.js to exchange the stored refresh token for a new access token, then call Microsoft Graph. Here's a sample Node.js backend implementation:

MSAL Node Setup

const msal = require('@azure/msal-node');

// Confidential client config (your backend is a confidential client)
const msalConfig = {
  auth: {
    clientId: "YOUR_CLIENT_ID",
    authority: "https://login.microsoftonline.com/YOUR_TENANT_ID",
    clientSecret: "YOUR_CLIENT_SECRET" // Generate this in your Azure AD app registration under "Certificates & secrets"
  }
};

const cca = new msal.ConfidentialClientApplication(msalConfig);

Exchange Refresh Token for Access Token

async function getAccessToken(refreshToken) {
  const tokenRequest = {
    refreshToken: refreshToken,
    scopes: ["user.read", "mail.read"] // Match the scopes used in the SPA login
  };

  try {
    const response = await cca.acquireTokenByRefreshToken(tokenRequest);
    // Optionally, update the stored refresh token with the new one returned (MSAL may issue a new refresh token)
    await updateRefreshTokenInDB(response.refreshToken);
    return response.accessToken;
  } catch (err) {
    console.error("Failed to get access token:", err);
    // Handle refresh token expiration (e.g., notify user to re-login)
    throw err;
  }
}

Cron Job Implementation

const cron = require('node-cron');

// Schedule your cron job (example: runs daily at 2 AM)
cron.schedule('0 2 * * *', async () => {
  try {
    // Fetch the stored refresh token from your database
    const { refreshToken } = await getStoredRefreshTokenFromDB();
    const accessToken = await getAccessToken(refreshToken);
    
    // Call Microsoft Graph API
    const graphResponse = await fetch("https://graph.microsoft.com/v1.0/me/messages", {
      headers: { Authorization: `Bearer ${accessToken}` }
    });
    const data = await graphResponse.json();
    
    // Process and update your data as needed
    console.log("Fetched data from Graph:", data);
  } catch (err) {
    console.error("Cron job failed:", err);
  }
});

Key Security & Implementation Notes

  • Encrypt Refresh Tokens: Always encrypt refresh tokens when storing them in your database—never store them as plain text.
  • Handle Token Expiry: Refresh tokens do expire (and can be revoked), so your backend should handle cases where acquireTokenByRefreshToken fails (e.g., trigger a user re-login flow or alert an admin).
  • Permission Consent: Ensure the user has consented to all scopes you're requesting (including offline_access)—you may need to prompt for consent on login if permissions haven't been granted yet.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 09:43:15