/** * @copyright nhcarrigan * @license Naomi's Public License * @author Naomi Carrigan * * Simple local server to authenticate with Facebook and obtain a Page Access Token. * Run with: node facebookAuth.js * Make sure to set FACEBOOK_APP_ID and FACEBOOK_APP_SECRET environment variables. */ import http from "http"; import { URL } from "url"; const PORT = 3000; const REDIRECT_URI = `http://localhost:${PORT}/callback`; /** * Creates the Facebook OAuth authorization URL. * @param {string} appId - The Facebook App ID. * @returns {string} The authorization URL. */ const getAuthUrl = (appId) => { const params = new URLSearchParams({ client_id: appId, redirect_uri: REDIRECT_URI, scope: "pages_manage_posts,pages_show_list", response_type: "code", }); return `https://www.facebook.com/v21.0/dialog/oauth?${params.toString()}`; }; /** * Exchanges an authorization code for an access token. * @param {string} code - The authorization code from Facebook. * @param {string} appId - The Facebook App ID. * @param {string} appSecret - The Facebook App Secret. * @returns {Promise<{access_token: string, expires_in?: number}>} The access token response. */ const exchangeCodeForToken = async (code, appId, appSecret) => { const params = new URLSearchParams({ client_id: appId, client_secret: appSecret, redirect_uri: REDIRECT_URI, code: code, }); const response = await fetch( `https://graph.facebook.com/v21.0/oauth/access_token?${params.toString()}`, ); return await response.json(); }; /** * Exchanges a short-lived token for a long-lived token. * @param {string} shortLivedToken - The short-lived access token. * @param {string} appId - The Facebook App ID. * @param {string} appSecret - The Facebook App Secret. * @returns {Promise<{access_token: string, expires_in?: number}>} The long-lived token response. */ const exchangeForLongLivedToken = async (shortLivedToken, appId, appSecret) => { const params = new URLSearchParams({ grant_type: "fb_exchange_token", client_id: appId, client_secret: appSecret, fb_exchange_token: shortLivedToken, }); const response = await fetch( `https://graph.facebook.com/v21.0/oauth/access_token?${params.toString()}`, ); return await response.json(); }; /** * Gets the user's pages. * @param {string} accessToken - The user access token. * @returns {Promise} Array of pages the user manages. */ const getUserPages = async (accessToken) => { const response = await fetch( `https://graph.facebook.com/v21.0/me/accounts?access_token=${accessToken}`, ); const data = await response.json(); return data.data || []; }; /** * Gets a Page Access Token for a specific page. * @param {string} pageId - The page ID. * @param {string} userAccessToken - The user access token. * @returns {Promise} The Page Access Token. */ const getPageAccessToken = async (pageId, userAccessToken) => { const response = await fetch( `https://graph.facebook.com/v21.0/${pageId}?fields=access_token&access_token=${userAccessToken}`, ); const data = await response.json(); return data.access_token; }; /** * Exchanges a short-lived Page Access Token for a long-lived one. * @param {string} pageAccessToken - The short-lived Page Access Token. * @param {string} appId - The Facebook App ID. * @param {string} appSecret - The Facebook App Secret. * @returns {Promise<{access_token: string, expires_in?: number}>} The long-lived Page Access Token. */ const exchangePageTokenForLongLived = async ( pageAccessToken, appId, appSecret, ) => { const params = new URLSearchParams({ grant_type: "fb_exchange_token", client_id: appId, client_secret: appSecret, fb_exchange_token: pageAccessToken, }); const response = await fetch( `https://graph.facebook.com/v21.0/oauth/access_token?${params.toString()}`, ); return await response.json(); }; /** * Sends an HTML response. * @param {http.ServerResponse} res - The HTTP response object. * @param {number} statusCode - The HTTP status code. * @param {string} html - The HTML content to send. */ const sendHtml = (res, statusCode, html) => { res.writeHead(statusCode, { "Content-Type": "text/html" }); res.end(html); }; /** * Sends a JSON response. * @param {http.ServerResponse} res - The HTTP response object. * @param {number} statusCode - The HTTP status code. * @param {object} data - The JSON data to send. */ const sendJson = (res, statusCode, data) => { res.writeHead(statusCode, { "Content-Type": "application/json" }); res.end(JSON.stringify(data, null, 2)); }; const appId = process.env.FACEBOOK_APP_ID; const appSecret = process.env.FACEBOOK_APP_SECRET; if (!appId || !appSecret) { console.error( "Error: FACEBOOK_APP_ID and FACEBOOK_APP_SECRET environment variables must be set.", ); console.error( "Example: FACEBOOK_APP_ID=your_app_id FACEBOOK_APP_SECRET=your_secret node facebookAuth.js", ); process.exit(1); } const server = http.createServer(async (req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`); // Root route - show auth link if (url.pathname === "/") { const authUrl = getAuthUrl(appId); const html = ` Facebook Page Token Generator

šŸ” Facebook Page Token Generator

Click the button below to authenticate with Facebook and get your Page Access Token.

Authenticate with Facebook
Note: Make sure you're an admin of the Facebook Page you want to post to.
`; return sendHtml(res, 200, html); } // Callback route - handle OAuth callback if (url.pathname === "/callback") { const code = url.searchParams.get("code"); const error = url.searchParams.get("error"); if (error) { const html = ` Authentication Error

āŒ Authentication Error

Error: ${error}

${url.searchParams.get("error_description") || ""}

Try again

`; return sendHtml(res, 400, html); } if (!code) { return sendHtml( res, 400, "

Error

No authorization code received.

Try again", ); } try { // Step 1: Exchange code for short-lived user token const tokenResponse = await exchangeCodeForToken(code, appId, appSecret); if (tokenResponse.error) { throw new Error( tokenResponse.error.message || "Failed to exchange code for token", ); } const shortLivedUserToken = tokenResponse.access_token; // Step 2: Exchange for long-lived user token const longLivedUserTokenResponse = await exchangeForLongLivedToken( shortLivedUserToken, appId, appSecret, ); if (longLivedUserTokenResponse.error) { throw new Error( longLivedUserTokenResponse.error.message || "Failed to exchange for long-lived token", ); } const longLivedUserToken = longLivedUserTokenResponse.access_token; // Step 3: Get user's pages const pages = await getUserPages(longLivedUserToken); if (pages.length === 0) { return sendHtml( res, 200, ` No Pages Found

āš ļø No Pages Found

You don't have access to any Facebook Pages, or you're not an admin of any pages.

Try again

`, ); } // Step 4: Get Page Access Tokens and exchange for long-lived const pageTokens = []; for (const page of pages) { const pageAccessToken = await getPageAccessToken( page.id, longLivedUserToken, ); const longLivedPageTokenResponse = await exchangePageTokenForLongLived( pageAccessToken, appId, appSecret, ); if (!longLivedPageTokenResponse.error) { pageTokens.push({ pageId: page.id, pageName: page.name, accessToken: longLivedPageTokenResponse.access_token, expiresIn: longLivedPageTokenResponse.expires_in, }); } } // Display results const pagesHtml = pageTokens .map( (pt) => `

${pt.pageName}

Page ID: ${pt.pageId}

Access Token:

Expires in: ${pt.expiresIn ? `${Math.floor(pt.expiresIn / 86400)} days` : "Never (as long as admin access is maintained)"}

`, ) .join(""); const html = ` Success! Your Page Tokens

āœ… Success!

Your Page Access Tokens:

Copy these tokens and add them to your environment variables. Use the Page Access Token for the page you want to post to.

${pagesHtml}

āš ļø Important:

  • Store these tokens securely (like your other API credentials)
  • Page Access Tokens don't expire as long as you remain an admin
  • Add the token to your environment variables as FACEBOOK_PAGE_ACCESS_TOKEN
  • You'll also need the Page ID as FACEBOOK_PAGE_ID

Start over

`; return sendHtml(res, 200, html); } catch (error) { const html = ` Error

āŒ Error

Error: ${error.message}

Try again

`; return sendHtml(res, 500, html); } } // 404 sendHtml(res, 404, "

Not Found

Go home

"); }); server.listen(PORT, () => { console.log(`\nšŸš€ Facebook Auth Server running at http://localhost:${PORT}`); console.log(`\nšŸ“‹ Make sure you've set:`); console.log(` - FACEBOOK_APP_ID`); console.log(` - FACEBOOK_APP_SECRET`); console.log(`\nšŸ”— Open http://localhost:${PORT} in your browser to start!\n`); });