@nevermined-io/cli 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-command.d.ts +11 -1
- package/dist/base-command.d.ts.map +1 -1
- package/dist/base-command.js +14 -0
- package/dist/base-command.js.map +1 -1
- package/dist/commands/cards/delegate.d.ts +26 -0
- package/dist/commands/cards/delegate.d.ts.map +1 -0
- package/dist/commands/cards/delegate.js +87 -0
- package/dist/commands/cards/delegate.js.map +1 -0
- package/dist/commands/cards/enroll.d.ts +25 -0
- package/dist/commands/cards/enroll.d.ts.map +1 -0
- package/dist/commands/cards/enroll.js +88 -0
- package/dist/commands/cards/enroll.js.map +1 -0
- package/dist/commands/cards/setup.d.ts +28 -0
- package/dist/commands/cards/setup.d.ts.map +1 -0
- package/dist/commands/cards/setup.js +113 -0
- package/dist/commands/cards/setup.js.map +1 -0
- package/dist/commands/login.js +1 -26
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/organizations/get-my-memberships.d.ts +15 -0
- package/dist/commands/organizations/get-my-memberships.d.ts.map +1 -0
- package/dist/commands/organizations/get-my-memberships.js +25 -0
- package/dist/commands/organizations/get-my-memberships.js.map +1 -0
- package/dist/commands/organizations/get-organization-activity.d.ts +18 -0
- package/dist/commands/organizations/get-organization-activity.d.ts.map +1 -0
- package/dist/commands/organizations/get-organization-activity.js +32 -0
- package/dist/commands/organizations/get-organization-activity.js.map +1 -0
- package/dist/generator/command-generator.d.ts.map +1 -1
- package/dist/generator/command-generator.js +3 -1
- package/dist/generator/command-generator.js.map +1 -1
- package/dist/utils/browser.d.ts +14 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +41 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/orgs.d.ts +36 -0
- package/dist/utils/orgs.d.ts.map +1 -0
- package/dist/utils/orgs.js +65 -0
- package/dist/utils/orgs.js.map +1 -0
- package/dist/utils/widget-redirect-flow.d.ts +83 -0
- package/dist/utils/widget-redirect-flow.d.ts.map +1 -0
- package/dist/utils/widget-redirect-flow.js +259 -0
- package/dist/utils/widget-redirect-flow.js.map +1 -0
- package/oclif.manifest.json +421 -57
- package/package.json +1 -1
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { createServer } from 'http';
|
|
2
|
+
import { randomBytes, timingSafeEqual } from 'crypto';
|
|
3
|
+
import { openBrowser } from './browser.js';
|
|
4
|
+
const SELF_MINT_FETCH_TIMEOUT_MS = 15_000;
|
|
5
|
+
/**
|
|
6
|
+
* Default timeout for a redirect-mode CLI flow. Mirrors the existing
|
|
7
|
+
* `nvm login` callback timeout — 5 minutes is enough for the user to
|
|
8
|
+
* tab into the browser, complete a card enrolment + delegation, and
|
|
9
|
+
* land back at the CLI.
|
|
10
|
+
*/
|
|
11
|
+
const REDIRECT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
12
|
+
/**
|
|
13
|
+
* Constant-time string equality for the CSRF `state` nonce. The state we
|
|
14
|
+
* issue is a 32-char hex string (`randomBytes(16).toString('hex')`), so
|
|
15
|
+
* we ALWAYS allocate fixed 16-byte buffers from `hex` regardless of the
|
|
16
|
+
* caller-supplied value's encoding. Computing buffer length from string
|
|
17
|
+
* length would diverge on non-ASCII input (`a.length` is UTF-16 code
|
|
18
|
+
* units; `Buffer.from(a, 'utf8').length` is byte count), and a crafted
|
|
19
|
+
* non-hex `received` could otherwise throw inside `timingSafeEqual`.
|
|
20
|
+
*
|
|
21
|
+
* Inputs must already have been verified to be 32 hex chars by the
|
|
22
|
+
* caller (or we reject up front).
|
|
23
|
+
*/
|
|
24
|
+
function safeEqualHexState(received, expected) {
|
|
25
|
+
if (!/^[0-9a-f]{32}$/i.test(received) || !/^[0-9a-f]{32}$/i.test(expected))
|
|
26
|
+
return false;
|
|
27
|
+
return timingSafeEqual(Buffer.from(received, 'hex'), Buffer.from(expected, 'hex'));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Shared redirect-mode handshake for any CLI command that hands the user
|
|
31
|
+
* off to an `/embed/*` page and waits for a localhost callback.
|
|
32
|
+
*
|
|
33
|
+
* Flow:
|
|
34
|
+
* 1. Bind a one-shot HTTP server on `127.0.0.1:0` (the OS picks a free port).
|
|
35
|
+
* 2. Compute `returnUrl = http://127.0.0.1:<port>/callback` and hand it
|
|
36
|
+
* to `opts.mintSession`. The caller mints a widget session bound to
|
|
37
|
+
* that exact returnUrl (which the backend can validate against the
|
|
38
|
+
* session-specific allow-list at creation time). We use the literal
|
|
39
|
+
* `127.0.0.1` rather than `localhost` because the server binds to
|
|
40
|
+
* `127.0.0.1` and Node 17+ resolves `localhost` to `::1` first on
|
|
41
|
+
* modern hosts — the browser would stall on the IPv6 attempt before
|
|
42
|
+
* falling back to IPv4.
|
|
43
|
+
* 3. Open the browser at `{frontend}/embed/<path>?sessionToken=…&returnUrl=…&state=<rand>`.
|
|
44
|
+
* 4. Resolve when the embed page redirects to `/callback?…&state=<echo>`.
|
|
45
|
+
* `state` is compared in constant time.
|
|
46
|
+
*
|
|
47
|
+
* Rejects on bind failure, mint failure, 5-minute timeout, or
|
|
48
|
+
* state-mismatched callback (the bad request gets a styled error page
|
|
49
|
+
* and the server stays alive — the legitimate callback can still land).
|
|
50
|
+
*/
|
|
51
|
+
export async function runWidgetRedirectFlow(opts) {
|
|
52
|
+
const state = randomBytes(16).toString('hex');
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
let resolved = false;
|
|
55
|
+
let timeout;
|
|
56
|
+
const finalize = (err, value) => {
|
|
57
|
+
if (resolved)
|
|
58
|
+
return;
|
|
59
|
+
resolved = true;
|
|
60
|
+
if (timeout)
|
|
61
|
+
clearTimeout(timeout);
|
|
62
|
+
server.close();
|
|
63
|
+
if (err)
|
|
64
|
+
reject(err);
|
|
65
|
+
else if (value)
|
|
66
|
+
resolve(value);
|
|
67
|
+
};
|
|
68
|
+
const server = createServer((req, res) => {
|
|
69
|
+
const url = new URL(req.url || '/', 'http://localhost');
|
|
70
|
+
if (url.pathname !== '/callback') {
|
|
71
|
+
res.writeHead(404).end('Not found');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const receivedState = url.searchParams.get('state');
|
|
75
|
+
if (!receivedState || !safeEqualHexState(receivedState, state)) {
|
|
76
|
+
// INTENTIONALLY do NOT close the server here. The state nonce
|
|
77
|
+
// is 128 bits of randomness, so brute-forcing one bad callback
|
|
78
|
+
// per request is infeasible. Closing on first 400 would let an
|
|
79
|
+
// attacker (or a misconfigured redirect) DoS the legitimate
|
|
80
|
+
// browser callback that's about to arrive. The 5-minute timer
|
|
81
|
+
// is the upper bound on how long we'll wait either way.
|
|
82
|
+
res.writeHead(400, { 'Content-Type': 'text/html' }).end(errorHtml('Callback rejected', 'State mismatch. This callback did not match the request that started the flow — close this tab and re-run the command.'));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Convert URLSearchParams → plain object, dropping the bookkeeping
|
|
86
|
+
// `state` field (the caller doesn't need to re-validate it).
|
|
87
|
+
const query = {};
|
|
88
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
89
|
+
if (key === 'state')
|
|
90
|
+
continue;
|
|
91
|
+
query[key] = value;
|
|
92
|
+
}
|
|
93
|
+
res.writeHead(200, { 'Content-Type': 'text/html' }).end(successHtml(opts.successPageTitle ?? 'All done', 'You can close this tab and return to your terminal.'));
|
|
94
|
+
finalize(undefined, { state, query });
|
|
95
|
+
});
|
|
96
|
+
timeout = setTimeout(() => {
|
|
97
|
+
finalize(new Error(opts.timeoutMessage ?? 'Browser flow timed out after 5 minutes. Please try again.'));
|
|
98
|
+
}, REDIRECT_TIMEOUT_MS);
|
|
99
|
+
server.on('error', (err) => {
|
|
100
|
+
finalize(new Error(`Failed to start local callback server: ${err.message}`));
|
|
101
|
+
});
|
|
102
|
+
server.listen(0, '127.0.0.1', () => {
|
|
103
|
+
const addr = server.address();
|
|
104
|
+
if (!addr || typeof addr === 'string') {
|
|
105
|
+
finalize(new Error('Failed to obtain local callback port'));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Symmetric with the server bind above (`127.0.0.1` only). Using
|
|
109
|
+
// the `localhost` alias here would cause an IPv6 stall on modern
|
|
110
|
+
// Linux/macOS where Node's `dns.lookup` prefers `::1`. The backend
|
|
111
|
+
// returnUrl allow-list accepts both forms.
|
|
112
|
+
const returnUrl = `http://127.0.0.1:${addr.port}/callback`;
|
|
113
|
+
// Mint the session now that returnUrl is known. The backend's
|
|
114
|
+
// allow-list check at session creation can validate the URL the
|
|
115
|
+
// browser will actually be redirected to.
|
|
116
|
+
void (async () => {
|
|
117
|
+
let sessionToken;
|
|
118
|
+
try {
|
|
119
|
+
const minted = await opts.mintSession({ returnUrl });
|
|
120
|
+
sessionToken = minted.sessionToken;
|
|
121
|
+
}
|
|
122
|
+
catch (mintErr) {
|
|
123
|
+
finalize(mintErr instanceof Error ? mintErr : new Error(String(mintErr)));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const browserUrl = buildEmbedUrl({
|
|
127
|
+
frontendUrl: opts.frontendUrl,
|
|
128
|
+
embedPath: opts.embedPath,
|
|
129
|
+
sessionToken,
|
|
130
|
+
returnUrl,
|
|
131
|
+
state,
|
|
132
|
+
extra: opts.extraSearchParams,
|
|
133
|
+
});
|
|
134
|
+
if (opts.noBrowser) {
|
|
135
|
+
opts.log('Open this URL in your browser to continue:');
|
|
136
|
+
opts.log('');
|
|
137
|
+
opts.log(browserUrl);
|
|
138
|
+
opts.log('');
|
|
139
|
+
opts.log('Waiting for completion...');
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
opts.log('Opening browser...');
|
|
143
|
+
openBrowser(browserUrl).catch(() => {
|
|
144
|
+
opts.log('Could not open the browser automatically. Open this URL manually:');
|
|
145
|
+
opts.log('');
|
|
146
|
+
opts.log(browserUrl);
|
|
147
|
+
});
|
|
148
|
+
opts.log('Waiting for completion...');
|
|
149
|
+
}
|
|
150
|
+
})();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function buildEmbedUrl(opts) {
|
|
155
|
+
const url = new URL(opts.embedPath, opts.frontendUrl);
|
|
156
|
+
url.searchParams.set('sessionToken', opts.sessionToken);
|
|
157
|
+
url.searchParams.set('returnUrl', opts.returnUrl);
|
|
158
|
+
url.searchParams.set('state', opts.state);
|
|
159
|
+
if (opts.extra) {
|
|
160
|
+
for (const [k, v] of Object.entries(opts.extra)) {
|
|
161
|
+
url.searchParams.set(k, v);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return url.toString();
|
|
165
|
+
}
|
|
166
|
+
function successHtml(title, message) {
|
|
167
|
+
// Plain HTML, no third-party assets, no scripts. The page is shown
|
|
168
|
+
// once and the user closes the tab; keep it self-contained so a
|
|
169
|
+
// captive-portal-style network doesn't break the success state.
|
|
170
|
+
return buildHtmlPage(title, message, '#f8f9fa', '#0f5132', '#d1e7dd');
|
|
171
|
+
}
|
|
172
|
+
function errorHtml(title, message) {
|
|
173
|
+
// Same shell as the success page (so the layout looks consistent if
|
|
174
|
+
// the user sees both back-to-back), but tinted red so a state mismatch
|
|
175
|
+
// or other 4xx response is visually distinct from "we're done".
|
|
176
|
+
return buildHtmlPage(title, message, '#f8f9fa', '#842029', '#f8d7da');
|
|
177
|
+
}
|
|
178
|
+
function buildHtmlPage(title, message, pageBg, fgColor, panelBg) {
|
|
179
|
+
return `<!doctype html>
|
|
180
|
+
<html><head><meta charset="utf-8"><title>${escapeHtml(title)}</title></head>
|
|
181
|
+
<body style="font-family: system-ui, sans-serif; display:flex; align-items:center; justify-content:center; min-height:100vh; margin:0; background:${pageBg};">
|
|
182
|
+
<div style="text-align:center; max-width: 480px; padding: 24px; background:${panelBg}; color:${fgColor}; border-radius:8px;">
|
|
183
|
+
<h2 style="margin: 0 0 12px;">${escapeHtml(title)}</h2>
|
|
184
|
+
<p style="margin: 0;">${escapeHtml(message)}</p>
|
|
185
|
+
</div>
|
|
186
|
+
</body></html>`;
|
|
187
|
+
}
|
|
188
|
+
function escapeHtml(value) {
|
|
189
|
+
return value
|
|
190
|
+
.replace(/&/g, '&')
|
|
191
|
+
.replace(/</g, '<')
|
|
192
|
+
.replace(/>/g, '>')
|
|
193
|
+
.replace(/"/g, '"')
|
|
194
|
+
.replace(/'/g, ''');
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Hosts a request to `mintSelfWidgetSession` may be sent over plaintext
|
|
198
|
+
* without warning. Loopback addresses are always safe; every other host
|
|
199
|
+
* must use `https:` because we're about to send the user's NVM API key
|
|
200
|
+
* in the `Authorization` header. The `custom` environment variable
|
|
201
|
+
* (`NVM_BACKEND_URL`) is the only realistic way a user could accidentally
|
|
202
|
+
* point this at a plaintext non-localhost URL.
|
|
203
|
+
*/
|
|
204
|
+
const LOOPBACK_HOSTNAMES = new Set([
|
|
205
|
+
'localhost',
|
|
206
|
+
'127.0.0.1',
|
|
207
|
+
'::1',
|
|
208
|
+
'[::1]',
|
|
209
|
+
]);
|
|
210
|
+
export async function mintSelfWidgetSession(args) {
|
|
211
|
+
const url = new URL('/api/v1/widgets/session/self', args.backendUrl);
|
|
212
|
+
// Refuse to send the API key over plaintext to a non-loopback host.
|
|
213
|
+
// All four built-in environments are `https://`, so this only bites
|
|
214
|
+
// when a user has set `NVM_BACKEND_URL` to a custom plaintext
|
|
215
|
+
// endpoint — exactly the case where they'd otherwise leak the key
|
|
216
|
+
// without realising.
|
|
217
|
+
if (url.protocol === 'http:' &&
|
|
218
|
+
!LOOPBACK_HOSTNAMES.has(url.hostname.toLowerCase())) {
|
|
219
|
+
throw new Error(`Refusing to send the NVM API key over plaintext to ${url.host}. Set NVM_BACKEND_URL to an https:// endpoint, or use a loopback host (localhost / 127.0.0.1 / [::1]).`);
|
|
220
|
+
}
|
|
221
|
+
// 15s ceiling for the request — without it, an unreachable backend
|
|
222
|
+
// (DNS failure, TLS handshake hang, network partition) leaves the CLI
|
|
223
|
+
// frozen with no output. `AbortSignal.timeout` (Node 17.3+) is the
|
|
224
|
+
// idiomatic replacement for a hand-rolled `AbortController` + setTimeout.
|
|
225
|
+
let response;
|
|
226
|
+
try {
|
|
227
|
+
response = await fetch(url, {
|
|
228
|
+
method: 'POST',
|
|
229
|
+
headers: {
|
|
230
|
+
'Content-Type': 'application/json',
|
|
231
|
+
Authorization: `Bearer ${args.nvmApiKey}`,
|
|
232
|
+
},
|
|
233
|
+
body: JSON.stringify({
|
|
234
|
+
orgId: args.orgId,
|
|
235
|
+
...(args.returnUrl ? { returnUrl: args.returnUrl } : {}),
|
|
236
|
+
}),
|
|
237
|
+
signal: AbortSignal.timeout(SELF_MINT_FETCH_TIMEOUT_MS),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch (cause) {
|
|
241
|
+
if (cause instanceof Error && (cause.name === 'TimeoutError' || cause.name === 'AbortError')) {
|
|
242
|
+
throw new Error(`Self-mint widget session request timed out after ${SELF_MINT_FETCH_TIMEOUT_MS / 1000}s — check that ${args.backendUrl} is reachable.`);
|
|
243
|
+
}
|
|
244
|
+
throw cause;
|
|
245
|
+
}
|
|
246
|
+
if (!response.ok) {
|
|
247
|
+
const detail = await response
|
|
248
|
+
.json()
|
|
249
|
+
.catch(() => ({ message: response.statusText }));
|
|
250
|
+
const message = detail.message ??
|
|
251
|
+
`Self-mint widget session failed (HTTP ${response.status})`;
|
|
252
|
+
const err = new Error(message);
|
|
253
|
+
err.status = response.status;
|
|
254
|
+
err.apiCode = detail.code;
|
|
255
|
+
throw err;
|
|
256
|
+
}
|
|
257
|
+
return (await response.json());
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=widget-redirect-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-redirect-flow.js","sourceRoot":"","sources":["../../src/utils/widget-redirect-flow.ts"],"names":[],"mappings":"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"}
|