Files
openclaw/skills/gmail-reader/reauth-outlook.cjs
Clawd Bot ca9b510922 chore: align with upstream openclaw/openclaw and overlay local additions
- Reset master to upstream/main (16,697 commits)
- Overlay 2,271 local-only files (skills, tools, workspace, configs, apps)
- Restore IDENTITY.md and USER.md templates
- Build verified, gateway running, Discord working

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 07:40:46 +01:00

180 lines
5.6 KiB
JavaScript

#!/usr/bin/env node
const fs = require("fs");
const https = require("https");
const { homedir } = require("os");
const { join } = require("path");
const { createServer } = require("http");
const CREDS_PATH = join(homedir(), ".openclaw/secrets/outlook-oauth.json");
const REDIRECT_URI = "http://localhost:8765";
// Read existing credentials to get client_id
let existingCreds = {};
try {
existingCreds = JSON.parse(fs.readFileSync(CREDS_PATH, "utf8"));
} catch (err) {
console.error("❌ Could not read existing credentials:", err.message);
process.exit(1);
}
const CLIENT_ID = existingCreds.client_id;
// Updated scopes with ReadWrite permissions
const SCOPES = [
"https://graph.microsoft.com/Mail.ReadWrite",
"https://graph.microsoft.com/MailboxSettings.ReadWrite",
"offline_access",
].join(" ");
function httpsRequest(url, options = {}) {
return new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
if (res.statusCode >= 400) {
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
} else {
resolve(JSON.parse(data));
}
});
});
req.on("error", reject);
if (options.body) req.write(options.body);
req.end();
});
}
async function exchangeCode(code) {
const body = new URLSearchParams({
client_id: CLIENT_ID,
code: code,
redirect_uri: REDIRECT_URI,
grant_type: "authorization_code",
scope: SCOPES,
}).toString();
const data = await httpsRequest("https://login.microsoftonline.com/consumers/oauth2/v2.0/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(body),
},
body,
});
return {
client_id: CLIENT_ID,
refresh_token: data.refresh_token,
access_token: data.access_token,
scope: SCOPES,
timestamp: new Date().toISOString(),
};
}
async function main() {
console.log("🔐 Outlook Re-authorization (ReadWrite permissions)\n");
console.log("This will grant permissions to:");
console.log(" - Read emails");
console.log(" - Move/delete emails");
console.log(" - Create inbox rules");
console.log(" - Block spam automatically\n");
const authUrl = `https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?${new URLSearchParams(
{
client_id: CLIENT_ID,
response_type: "code",
redirect_uri: REDIRECT_URI,
scope: SCOPES,
response_mode: "query",
prompt: "consent", // Force consent screen to show new permissions
},
)}`;
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
console.log("📋 Open this URL in your browser:\n");
console.log(authUrl);
console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
console.log("Waiting for authorization...\n");
return new Promise((resolve, reject) => {
const server = createServer(async (req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`);
const code = url.searchParams.get("code");
const error = url.searchParams.get("error");
if (error) {
res.writeHead(200, { "Content-Type": "text/html" });
res.end(`<h1>❌ Authorization failed</h1><p>${error}</p><p>You can close this window.</p>`);
server.close();
reject(new Error(error));
return;
}
if (!code) {
res.writeHead(400);
res.end("Missing code parameter");
return;
}
try {
console.log("✅ Authorization code received, exchanging for tokens...\n");
const creds = await exchangeCode(code);
fs.writeFileSync(CREDS_PATH, JSON.stringify(creds, null, 2));
fs.chmodSync(CREDS_PATH, 0o600);
console.log(`✅ Credentials saved to: ${CREDS_PATH}\n`);
console.log("Permissions granted:");
console.log(" ✓ Mail.ReadWrite");
console.log(" ✓ MailboxSettings.ReadWrite");
console.log(" ✓ offline_access\n");
res.writeHead(200, { "Content-Type": "text/html" });
res.end(`
<h1>✅ Authorization successful!</h1>
<p>You can close this window and return to the terminal.</p>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 2em; }
h1 { color: #28a745; }
</style>
`);
server.close();
resolve();
} catch (err) {
console.error("❌ Token exchange failed:", err.message);
res.writeHead(500, { "Content-Type": "text/html" });
res.end(`<h1>❌ Error</h1><p>${err.message}</p>`);
server.close();
reject(err);
}
});
server.listen(8765, () => {
console.log("🌐 Callback server listening on http://localhost:8765\n");
});
server.on("error", (err) => {
if (err.code === "EADDRINUSE") {
console.error("❌ Port 8765 is already in use. Stop other processes and try again.");
} else {
console.error("❌ Server error:", err.message);
}
reject(err);
});
});
}
main()
.then(() => {
console.log("✅ Outlook re-authorization complete!\n");
console.log("You can now block spam with: node block-dating-spam.cjs\n");
process.exit(0);
})
.catch((err) => {
console.error("❌ Re-authorization failed:", err.message);
process.exit(1);
});