UNPKG

@databricks/sql

Version:

Driver for connection to Databricks SQL via Thrift API.

151 lines 6.04 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const http_1 = __importDefault(require("http")); const openid_client_1 = require("openid-client"); const open_1 = __importDefault(require("open")); const IDBSQLLogger_1 = require("../../../contracts/IDBSQLLogger"); const OAuthScope_1 = require("./OAuthScope"); const AuthenticationError_1 = __importDefault(require("../../../errors/AuthenticationError")); async function defaultOpenAuthUrl(authUrl) { await (0, open_1.default)(authUrl); } class AuthorizationCode { constructor(options) { this.host = 'localhost'; this.client = options.client; this.context = options.context; this.options = options; } async fetch(scopes) { var _a; const verifierString = openid_client_1.generators.codeVerifier(32); const challengeString = openid_client_1.generators.codeChallenge(verifierString); const state = openid_client_1.generators.state(16); let receivedParams; const server = await this.createServer((req, res) => { const params = this.client.callbackParams(req); if (params.state === state) { receivedParams = params; res.writeHead(200); res.end(this.renderCallbackResponse()); server.stop(); } else { res.writeHead(404); res.end(); } }); const redirectUri = `http://${server.host}:${server.port}`; const authUrl = this.client.authorizationUrl({ response_type: 'code', response_mode: 'query', scope: scopes.join(OAuthScope_1.scopeDelimiter), code_challenge: challengeString, code_challenge_method: 'S256', state, redirect_uri: redirectUri, }); const openAuthUrl = (_a = this.options.openAuthUrl) !== null && _a !== void 0 ? _a : defaultOpenAuthUrl; await openAuthUrl(authUrl, defaultOpenAuthUrl); await server.stopped(); if (!receivedParams || !receivedParams.code) { if (receivedParams === null || receivedParams === void 0 ? void 0 : receivedParams.error) { const errorMessage = `OAuth error: ${receivedParams.error} ${receivedParams.error_description}`; throw new AuthenticationError_1.default(errorMessage); } throw new AuthenticationError_1.default(`No path parameters were returned to the callback at ${redirectUri}`); } return { code: receivedParams.code, verifier: verifierString, redirectUri }; } async createServer(requestHandler) { for (const port of this.options.ports) { const host = this.host; // eslint-disable-line prefer-destructuring try { const server = await this.startServer(host, port, requestHandler); // eslint-disable-line no-await-in-loop this.context.getLogger().log(IDBSQLLogger_1.LogLevel.info, `Listening for OAuth authorization callback at ${host}:${port}`); let resolveStopped; let rejectStopped; const stoppedPromise = new Promise((resolve, reject) => { resolveStopped = resolve; rejectStopped = reject; }); return { host, port, server, stop: () => this.stopServer(server).then(resolveStopped).catch(rejectStopped), stopped: () => stoppedPromise, }; } catch (error) { // if port already in use - try another one, otherwise re-throw an exception if (error instanceof Error && 'code' in error && error.code === 'EADDRINUSE') { this.context.getLogger().log(IDBSQLLogger_1.LogLevel.debug, `Failed to start server at ${host}:${port}: ${error.code}`); } else { throw error; } } } throw new AuthenticationError_1.default('Failed to start server: all ports are in use'); } createHttpServer(requestHandler) { return http_1.default.createServer(requestHandler); } async startServer(host, port, requestHandler) { const server = this.createHttpServer(requestHandler); return new Promise((resolve, reject) => { const errorListener = (error) => { server.off('error', errorListener); reject(error); }; server.on('error', errorListener); server.listen(port, host, () => { server.off('error', errorListener); resolve(server); }); }); } async stopServer(server) { if (!server.listening) { return; } return new Promise((resolve, reject) => { const errorListener = (error) => { server.off('error', errorListener); reject(error); }; server.on('error', errorListener); server.close(() => { server.off('error', errorListener); resolve(); }); }); } renderCallbackResponse() { const applicationName = 'Databricks Sql Connector'; return `<html lang="en"> <head> <title>Close this Tab</title> <style> body { font-family: "Barlow", Helvetica, Arial, sans-serif; padding: 20px; background-color: #f3f3f3; } </style> </head> <body> <h1>Please close this tab.</h1> <p> The ${applicationName} received a response. You may close this tab. </p> </body> </html>`; } } exports.default = AuthorizationCode; //# sourceMappingURL=AuthorizationCode.js.map