#!/usr/bin/env node // One-time OAuth2 authorization flow for Google Calendar. // Run this on the host to generate a token file for the server. // // Usage: // ccc-gcal-auth --credentials google-credentials.json --token google-token.json import { readFileSync, writeFileSync } from 'node:fs'; import { createServer } from 'node:http'; import { google } from 'googleapis'; const SCOPES = ['https://www.googleapis.com/auth/calendar']; const PORT = 3016; const REDIRECT_URI = `http://localhost:${PORT}/callback`; function get_arg(argv, flag) { const i = argv.indexOf(flag); return i !== -1 ? argv[i + 1] : null; } const credentials_path = get_arg(process.argv, '--credentials'); const token_path = get_arg(process.argv, '--token'); if (!credentials_path || !token_path) { console.error('Usage: ccc-gcal-auth --credentials --token '); process.exit(1); } let credentials; try { credentials = JSON.parse(readFileSync(credentials_path, 'utf8')); } catch (err) { console.error(`Cannot read credentials file: ${err.message}`); process.exit(1); } const { client_id, client_secret } = credentials.installed ?? credentials.web; const oauth2_client = new google.auth.OAuth2(client_id, client_secret, REDIRECT_URI); const auth_url = oauth2_client.generateAuthUrl({ access_type: 'offline', scope: SCOPES, prompt: 'consent', }); console.log('Open this URL in your browser to authorize Google Calendar access:\n'); console.log(auth_url); console.log(`\nListening for redirect on http://localhost:${PORT}/callback ...`); const server = createServer(async (req, res) => { let url; try { url = new URL(req.url, `http://localhost:${PORT}`); } catch (_) { res.end('Bad request'); return; } if (url.pathname !== '/callback') { res.end('Not found'); return; } const code = url.searchParams.get('code'); if (!code) { res.writeHead(400); res.end('Missing code parameter'); return; } try { const { tokens } = await oauth2_client.getToken(code); writeFileSync(token_path, JSON.stringify(tokens, null, '\t') + '\n', 'utf8'); res.end('Authorization successful. You may close this tab.'); console.log(`\nToken written to ${token_path}`); server.close(); } catch (err) { res.writeHead(500); res.end(`Error: ${err.message}`); console.error(`Token exchange failed: ${err.message}`); server.close(); process.exit(1); } }); server.listen(PORT, '127.0.0.1');