require("dotenv").config(); const express = require("express"); const fs = require("fs"); const msal = require("@azure/msal-node"); const axios = require("axios"); const app = express(); const port = 3000; const cachePath = "./cache.json"; const msalConfig = { auth: { clientId: process.env.CLIENT_ID, authority: "https://login.microsoftonline.com/common", clientSecret: process.env.CLIENT_SECRET, }, }; const pca = new msal.ConfidentialClientApplication(msalConfig); const scopes = ["offline_access", "Calendars.ReadWrite"]; const redirectUri = "http://localhost:3000/redirect"; function loadTokenCache() { if (fs.existsSync(cachePath)) { const cache = fs.readFileSync(cachePath, "utf-8"); pca.getTokenCache().deserialize(cache); } } function saveTokenCache() { const cache = pca.getTokenCache().serialize(); fs.writeFileSync(cachePath, cache); } async function acquireTokenSilently(scopes) { loadTokenCache(); const accounts = await pca.getTokenCache().getAllAccounts(); const account = accounts[0]; if (!account) throw new Error("No account found in token cache"); const result = await pca.acquireTokenSilent({ account, scopes }); return result.accessToken; } async function getCalendarEvents(accessToken) { try { const response = await axios.get( "https://graph.microsoft.com/v1.0/me/events?$select=subject,body,bodyPreview,organizer,attendees,start,end,location", { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, } ); return response.data; } catch (error) { console.error("Error fetching calendar events:", error?.response?.status, error?.response?.statusText); return { error: "Failed to fetch calendar events" }; } } app.get("/", async (req, res) => { loadTokenCache(); const authCodeUrlParams = { scopes, redirectUri, prompt: "select_account", }; const url = await pca.getAuthCodeUrl(authCodeUrlParams); res.redirect(url); }); app.get("/redirect", async (req, res) => { const tokenRequest = { code: req.query.code, scopes, redirectUri, }; try { const result = await pca.acquireTokenByCode(tokenRequest); saveTokenCache(); const accessToken = await acquireTokenSilently(scopes); // uses refresh token silently const rawCache = pca.getTokenCache().serialize(); const parsedCache = JSON.parse(rawCache); const rtEntry = parsedCache.RefreshToken; const rtKey = Object.keys(rtEntry)[0]; const refreshToken = rtEntry[rtKey]?.secret || "Not found"; console.log("\nAccess Token:\n", accessToken); console.log("\nRefresh Token:\n", refreshToken); const newEvent = { subject: "Test event created in MS account", body: { contentType: "HTML", content: "Test event for repro purpose.", }, start: { dateTime: new Date(Date.now() + 3600000).toISOString(), // 1 hour from now timeZone: "UTC", }, end: { dateTime: new Date(Date.now() + 7200000).toISOString(), // 2 hours from now timeZone: "UTC", }, attendees: [], }; const eventResponse = await axios.post( "https://graph.microsoft.com/v1.0/me/events", newEvent, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, } ); console.log("\nCreated Event:\n", JSON.stringify(eventResponse.data, null, 2)); const calendarData = await getCalendarEvents(accessToken); console.log("\nCalendar Events:\n", JSON.stringify(calendarData, null, 2)); res.send(`
${JSON.stringify(eventResponse.data, null, 2)}
${JSON.stringify(calendarData, null, 2)}
`);
} catch (err) {
console.error("Redirect error:", err.response?.data || err.message);
res.status(500).send("Something went wrong while processing token or calendar operation.");
}
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});