diff --git a/package-lock.json b/package-lock.json index 9273b4b7..64e95321 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "debug": "^4.3.4", "mustache": "^4.2.0", + "open": "^10.1.0", "postman-request": "^2.88.1-postman.33", "tv4": "^1.3.0" }, @@ -1104,6 +1105,20 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -1393,6 +1408,32 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", @@ -1408,6 +1449,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2145,6 +2197,20 @@ "node": ">=8" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2175,6 +2241,23 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2240,6 +2323,20 @@ "node": ">=0.10.0" } }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -2936,6 +3033,23 @@ "wrappy": "1" } }, + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -3349,6 +3463,17 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", diff --git a/package.json b/package.json index 461c388c..53a64827 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "dependencies": { "debug": "^4.3.4", "mustache": "^4.2.0", + "open": "^10.1.0", "postman-request": "^2.88.1-postman.33", "tv4": "^1.3.0" }, diff --git a/src/commands.js b/src/commands.js index bfc955cd..cdf6164e 100644 --- a/src/commands.js +++ b/src/commands.js @@ -1258,6 +1258,73 @@ module.exports = { res: approleResponse, }, }, + oidcAuthUrl: { + method: 'POST', + path: '/auth/oidc/oidc/auth_url', + schema: { + req: { + type: 'object', + properties: { + redirect_uri: { + type: 'string' + }, + client_nonce: { + type: 'string' + }, + role: { + type: 'string' + } + }, + required: ['redirect_uri'] + }, + res: { + type: 'object', + properties: { + request_id: { + type: 'string' + }, + data: { + type: 'object', + properties: { + auth_url: { + type: 'string' + } + }, + required: ['auth_url'] + } + }, + required: ['request_id', 'data'] + } + } + }, + oidcCallback: { + method: 'GET', + path: '/auth/oidc/oidc/callback', + tokenSource: true, + schema: { + query: { + type: 'object', + properties: { + state: { + type: 'string', + }, + code: { + type: 'string', + }, + client_nonce: { + type: 'string' + } + }, + required: ['state', 'code'] + }, + res: { + type: 'object', + properties: { + auth, + } + } + } + }, health: { method: 'GET', path: '/sys/health', @@ -1334,4 +1401,4 @@ module.exports = { method: 'PUT', path: '/sys/step-down', }, -} \ No newline at end of file +} diff --git a/src/index.js b/src/index.js index faccfead..871c2453 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,8 @@ const originalCommands = require('./commands.js'); const originalMustache = require('mustache'); const util = require('util'); const request = require('postman-request'); +const { randomBytes } = require('crypto'); +const http = require('http'); class VaultError extends Error {} @@ -244,5 +246,52 @@ module.exports = (config = {}) => { const assignFunctions = (commandName) => generateFunction(commandName, commands[commandName]); Object.keys(commands).forEach(assignFunctions); + client['oidcFlow'] = () => import('open') + .then(({default: open}) => { + const oidcCallbackPath = '/oidc/callback'; + const serverConfig = { + host: 'localhost', + port: 8250, + protocol: 'http' + } + return new Promise((done, reject) => { + const client_nonce = randomBytes(20).toString('hex').slice(20); + + const server = http.createServer((req, res) => { + const responseUrl = new URL(req.url, `${serverConfig.protocol}://${serverConfig.host}`) + if (responseUrl.pathname === oidcCallbackPath) { + res.write('Signed in via your OIDC provider\nYou can now close this window and start using Vault.'); + res.end(); + const code = responseUrl.searchParams.get('code') + const state = responseUrl.searchParams.get('state') + client.oidcCallback({ + state, + code, + client_nonce, + }) + .then(() => { + server.close(done); + }) + .catch(reject) + } + if (!res.writableEnded) { + res.end(); + } + }); + + server.listen(serverConfig.port,() => {}); + + client.oidcAuthUrl({ + redirect_uri: `${serverConfig.protocol}://${serverConfig.host}:${serverConfig.port}${oidcCallbackPath}`, + client_nonce, + }) + .then((r) => { + console.log(`Complete the login via your OIDC provider. Launching browser to: \n\n${r.data.auth_url}\n\n if browser does not open automatically, please copy paste the above URL`) + open(r.data.auth_url) + }) + .catch(reject) + }) + }) + return client; };