import https from "https"; import { URL } from "url"; import { ProviderConfig } from "./OAuthProvider"; /** * Base class for OAuth providers with shared utilities * * Provides common functionality: * - Query string building * - HTTP request execution * - Configuration storage */ export abstract class OAuthProviderBase { protected config: ProviderConfig; constructor(config: ProviderConfig) { this.config = config; } /** * Build URL query string from parameters * * @param params - Key-value pairs to encode * @returns Encoded query string (without leading '?') */ protected buildQueryString(params: Record): string { const entries = Object.entries(params).map(([key, value]) => { if (Array.isArray(value)) { return `${encodeURIComponent(key)}=${encodeURIComponent(value.join(" "))}`; } return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; }); return entries.join("&"); } /** * Make HTTPS request to external API * * @param url - Full URL to request * @param options - Request options (method, headers, body) * @returns Parsed JSON response */ protected async makeHttpRequest( url: string, options: { method: "GET" | "POST"; headers?: Record; body?: string; } ): Promise { return new Promise((resolve, reject) => { const parsedUrl = new URL(url); const requestOptions = { hostname: parsedUrl.hostname, port: parsedUrl.port || 443, path: parsedUrl.pathname + parsedUrl.search, method: options.method, headers: { "User-Agent": "Flink-OAuth-Plugin", ...options.headers, }, }; const req = https.request(requestOptions, (res) => { let data = ""; res.on("data", (chunk) => { data += chunk; }); res.on("end", () => { try { const parsed = JSON.parse(data); if (res.statusCode && res.statusCode >= 400) { reject(new Error(`HTTP ${res.statusCode}: ${JSON.stringify(parsed)}`)); } else { resolve(parsed); } } catch (error) { reject(new Error(`Failed to parse response: ${data}`)); } }); }); req.on("error", (error) => { reject(error); }); if (options.body) { req.write(options.body); } req.end(); }); } }