/*********************************************************************************************************************************************************************** * File Name: get_gmail_refresh_token.js * Type of Program: One-time setup script (run locally, not deployed) * Description: Mints a long-lived Gmail API refresh token for the sending mailbox * (EMAIL_FROM, e.g. services.philpro@gmail.com), so services/email.service.js * can send mail over HTTPS via the Gmail API instead of SMTP. * * Prerequisites (one-time, in Google Cloud Console — same project as GOOGLE_CLIENT_ID): * 1. APIs & Services → Library → enable "Gmail API". * 2. APIs & Services → Credentials → open the OAuth client used for GOOGLE_CLIENT_ID * → Authorized redirect URIs → add: http://localhost:5555/oauth2callback * (You can remove this URI again after this script succeeds.) * * Usage: * node scripts/get_gmail_refresh_token.js * → prints an auth URL. Open it in a browser, sign in AS the EMAIL_FROM mailbox, * approve the "Send email on your behalf" consent screen. * → the script prints GMAIL_REFRESH_TOKEN — copy it into Render's env vars. * * Author: Kenneth Obsequio (@lash0000) * Date Created: Jul. 14, 2026 ***********************************************************************************************************************************************************************/ 'use strict'; require('dotenv').config(); const http = require('http'); const { OAuth2Client } = require('google-auth-library'); const REDIRECT_URI = 'http://localhost:5555/oauth2callback'; const SCOPE = 'https://www.googleapis.com/auth/gmail.send'; const clientId = process.env.GOOGLE_CLIENT_ID; const clientSecret = process.env.GOOGLE_CLIENT_SECRET; if (!clientId || !clientSecret) { console.error('Missing GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET in .env'); process.exit(1); } const client = new OAuth2Client(clientId, clientSecret, REDIRECT_URI); const authUrl = client.generateAuthUrl({ access_type: 'offline', prompt: 'consent', // forces a refresh_token even if this account consented before scope: [SCOPE], }); console.log('\nOpen this URL, sign in AS the EMAIL_FROM mailbox, and approve access:\n'); console.log(authUrl, '\n'); console.log(`Waiting for the redirect on ${REDIRECT_URI} ...\n`); const server = http.createServer(async (req, res) => { if (!req.url.startsWith('/oauth2callback')) { res.writeHead(404).end(); return; } const code = new URL(req.url, REDIRECT_URI).searchParams.get('code'); if (!code) { res.writeHead(400).end('Missing ?code — check the URL Google redirected you to.'); return; } try { const { tokens } = await client.getToken(code); res.writeHead(200, { 'Content-Type': 'text/plain' }).end('Done — check your terminal.'); console.log('GMAIL_REFRESH_TOKEN=' + tokens.refresh_token, '\n'); console.log('Copy the line above into Render\'s environment variables, then redeploy.'); } catch (err) { res.writeHead(500).end('Token exchange failed — see terminal.'); console.error('Token exchange failed:', err.response?.data || err.message); } finally { server.close(); } }); server.listen(5555);