@circleback/cli 0.3.4 → 0.3.5
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/auth/oauth.js +21 -16
- package/dist/auth/server.js +7 -10
- package/dist/commands/auth.js +2 -28
- package/dist/helpers/stdin.js +39 -0
- package/dist/helpers/url.js +33 -0
- package/package.json +1 -1
package/dist/auth/oauth.js
CHANGED
|
@@ -4,9 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.forceRefreshOAuthToken = exports.getValidOAuthTokens = exports.login = void 0;
|
|
7
|
-
const node_child_process_1 = require("node:child_process");
|
|
8
7
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
9
8
|
const constants_1 = require("../constants");
|
|
9
|
+
const stdin_1 = require("../helpers/stdin");
|
|
10
|
+
const url_1 = require("../helpers/url");
|
|
10
11
|
const types_1 = require("../types");
|
|
11
12
|
const server_1 = require("./server");
|
|
12
13
|
const tokenStorage_1 = require("./tokenStorage");
|
|
@@ -89,9 +90,10 @@ const refreshAccessToken = async (refreshToken, clientId) => {
|
|
|
89
90
|
}
|
|
90
91
|
return tokenData;
|
|
91
92
|
};
|
|
92
|
-
const login = async (
|
|
93
|
+
const login = async () => {
|
|
93
94
|
const { port, waitForCallback, shutdown } = await (0, server_1.startCallbackServer)();
|
|
94
95
|
const redirectUri = `http://127.0.0.1:${port}`;
|
|
96
|
+
const promptAbort = new AbortController();
|
|
95
97
|
try {
|
|
96
98
|
const clientId = await registerClient(redirectUri);
|
|
97
99
|
const { verifier, challenge } = generatePkce();
|
|
@@ -105,21 +107,24 @@ const login = async (options = {}) => {
|
|
|
105
107
|
authorizeUrl.searchParams.set('code_challenge', challenge);
|
|
106
108
|
authorizeUrl.searchParams.set('code_challenge_method', 'S256');
|
|
107
109
|
const url = authorizeUrl.toString();
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
110
|
+
await (0, url_1.openUrlInBrowser)(url);
|
|
111
|
+
const callback = waitForCallback();
|
|
112
|
+
const retryBrowserOnEnter = async () => {
|
|
113
|
+
if (await (0, stdin_1.promptToOpenAuthorizeUrl)(url, promptAbort.signal)) {
|
|
114
|
+
await (0, url_1.openUrlInBrowser)(url);
|
|
115
|
+
}
|
|
116
|
+
return callback;
|
|
117
|
+
};
|
|
118
|
+
const receivedCallback = await Promise.race([
|
|
119
|
+
callback,
|
|
120
|
+
retryBrowserOnEnter(),
|
|
121
|
+
]);
|
|
122
|
+
promptAbort.abort();
|
|
123
|
+
if (receivedCallback.state !== state) {
|
|
119
124
|
throw new Error('State mismatch. Possible CSRF attack.');
|
|
120
125
|
}
|
|
121
126
|
const tokenResponse = await exchangeCode({
|
|
122
|
-
code:
|
|
127
|
+
code: receivedCallback.code,
|
|
123
128
|
redirectUri,
|
|
124
129
|
clientId,
|
|
125
130
|
codeVerifier: verifier,
|
|
@@ -128,9 +133,9 @@ const login = async (options = {}) => {
|
|
|
128
133
|
(0, tokenStorage_1.saveCredentials)(tokens);
|
|
129
134
|
return tokens;
|
|
130
135
|
}
|
|
131
|
-
|
|
136
|
+
finally {
|
|
137
|
+
promptAbort.abort();
|
|
132
138
|
shutdown();
|
|
133
|
-
throw error;
|
|
134
139
|
}
|
|
135
140
|
};
|
|
136
141
|
exports.login = login;
|
package/dist/auth/server.js
CHANGED
|
@@ -57,17 +57,9 @@ const startCallbackServer = () => new Promise((resolve, reject) => {
|
|
|
57
57
|
response.end(SUCCESS_HTML);
|
|
58
58
|
callbackResolve({ code, state });
|
|
59
59
|
});
|
|
60
|
-
const timeout = setTimeout(() => {
|
|
61
|
-
shutdown();
|
|
62
|
-
callbackReject(new Error('Authentication timed out. Please try again.'));
|
|
63
|
-
}, constants_1.CALLBACK_TIMEOUT_MILLISECONDS);
|
|
64
|
-
let isShutdown = false;
|
|
65
60
|
const shutdown = () => {
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
isShutdown = true;
|
|
69
|
-
clearTimeout(timeout);
|
|
70
|
-
server.close();
|
|
61
|
+
if (server.listening)
|
|
62
|
+
server.close();
|
|
71
63
|
};
|
|
72
64
|
server.listen(0, '127.0.0.1', () => {
|
|
73
65
|
const address = server.address();
|
|
@@ -78,6 +70,11 @@ const startCallbackServer = () => new Promise((resolve, reject) => {
|
|
|
78
70
|
resolve({
|
|
79
71
|
port: address.port,
|
|
80
72
|
waitForCallback: async () => {
|
|
73
|
+
const timeout = setTimeout(() => {
|
|
74
|
+
shutdown();
|
|
75
|
+
callbackReject(new Error('Authentication timed out. Please try again.'));
|
|
76
|
+
}, constants_1.CALLBACK_TIMEOUT_MILLISECONDS);
|
|
77
|
+
server.once('close', () => clearTimeout(timeout));
|
|
81
78
|
const result = await callbackPromise;
|
|
82
79
|
shutdown();
|
|
83
80
|
return result;
|
package/dist/commands/auth.js
CHANGED
|
@@ -13,24 +13,6 @@ const tokenStorage_1 = require("../auth/tokenStorage");
|
|
|
13
13
|
const constants_1 = require("../constants");
|
|
14
14
|
const output_1 = require("../helpers/output");
|
|
15
15
|
const types_1 = require("../types");
|
|
16
|
-
const AUTHORIZATION_URL_FALLBACK_DELAY_MILLISECONDS = 500;
|
|
17
|
-
const getOpenUrlInstruction = () => process.platform === 'darwin' ? '⌘-click' : 'Ctrl-click';
|
|
18
|
-
const createAuthorizationUrlFallback = () => {
|
|
19
|
-
const abortController = new AbortController();
|
|
20
|
-
const schedule = (url) => {
|
|
21
|
-
const timeout = setTimeout(() => {
|
|
22
|
-
if (abortController.signal.aborted)
|
|
23
|
-
return;
|
|
24
|
-
(0, output_1.printText)(`Browser didn't open? ${getOpenUrlInstruction()} the URL below to log in.\n`, output_1.TextType.Default);
|
|
25
|
-
(0, output_1.printText)(chalk_1.default.cyan(url), output_1.TextType.Default);
|
|
26
|
-
}, AUTHORIZATION_URL_FALLBACK_DELAY_MILLISECONDS);
|
|
27
|
-
abortController.signal.addEventListener('abort', () => clearTimeout(timeout), { once: true });
|
|
28
|
-
};
|
|
29
|
-
return {
|
|
30
|
-
schedule,
|
|
31
|
-
cleanup: () => abortController.abort(),
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
16
|
const loginCommand = new commander_1.Command('login')
|
|
35
17
|
.description('Authenticate with Circleback.')
|
|
36
18
|
.option('--api-key', 'Authenticate with a Public API key')
|
|
@@ -54,16 +36,8 @@ const loginCommand = new commander_1.Command('login')
|
|
|
54
36
|
(0, output_1.printText)('API key authentication successful.', output_1.TextType.Success);
|
|
55
37
|
return;
|
|
56
38
|
}
|
|
57
|
-
(0, output_1.printText)('Opening your browser for authentication
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
await (0, oauth_1.login)({
|
|
61
|
-
onAuthorizeUrl: authorizationUrlFallback.schedule,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
finally {
|
|
65
|
-
authorizationUrlFallback.cleanup();
|
|
66
|
-
}
|
|
39
|
+
(0, output_1.printText)('Opening your browser for authentication…', output_1.TextType.Default);
|
|
40
|
+
await (0, oauth_1.login)();
|
|
67
41
|
(0, output_1.printText)('Authentication successful.', output_1.TextType.Success);
|
|
68
42
|
}
|
|
69
43
|
catch (error) {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.promptToOpenAuthorizeUrl = void 0;
|
|
7
|
+
const node_readline_1 = require("node:readline");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const output_1 = require("./output");
|
|
10
|
+
const string_1 = require("./string");
|
|
11
|
+
const promptToOpenAuthorizeUrl = async (url, signal, stdin = process.stdin) => {
|
|
12
|
+
const isInteractive = Boolean(stdin.isTTY && process.stdout.isTTY);
|
|
13
|
+
if (isInteractive) {
|
|
14
|
+
(0, output_1.printText)(`Browser didn't open? Press Enter to log in…`, output_1.TextType.Default);
|
|
15
|
+
(0, output_1.printText)('', output_1.TextType.Default);
|
|
16
|
+
}
|
|
17
|
+
(0, output_1.printText)(process.stdout.isTTY ? (0, string_1.link)(chalk_1.default.cyan.underline(url), url) : url, output_1.TextType.Default);
|
|
18
|
+
if (!isInteractive || signal?.aborted)
|
|
19
|
+
return false;
|
|
20
|
+
const readlineInterface = (0, node_readline_1.createInterface)({
|
|
21
|
+
input: stdin,
|
|
22
|
+
terminal: false,
|
|
23
|
+
signal,
|
|
24
|
+
});
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
readlineInterface.once('line', () => {
|
|
27
|
+
resolve(true);
|
|
28
|
+
readlineInterface.close();
|
|
29
|
+
});
|
|
30
|
+
readlineInterface.once('close', () => {
|
|
31
|
+
if (signal?.aborted) {
|
|
32
|
+
resolve(false);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
reject(new Error('Authentication canceled.'));
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
exports.promptToOpenAuthorizeUrl = promptToOpenAuthorizeUrl;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.openUrlInBrowser = void 0;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_events_1 = require("node:events");
|
|
6
|
+
const output_1 = require("./output");
|
|
7
|
+
const openUrlInBrowser = async (url) => {
|
|
8
|
+
const isWindows = process.platform === 'win32';
|
|
9
|
+
const escapedUrl = isWindows ? url.replace(/&/g, '^&') : url;
|
|
10
|
+
const [command, ...arguments_] = isWindows
|
|
11
|
+
? ['start', '', escapedUrl]
|
|
12
|
+
: process.platform === 'darwin'
|
|
13
|
+
? ['open', url]
|
|
14
|
+
: ['xdg-open', url];
|
|
15
|
+
const warnAboutBrowserFailure = () => (0, output_1.printTextToStderr)('Unable to open your browser. Open the URL manually.', output_1.TextType.Warning);
|
|
16
|
+
try {
|
|
17
|
+
const child = (0, node_child_process_1.spawn)(command, arguments_, {
|
|
18
|
+
stdio: 'ignore',
|
|
19
|
+
shell: isWindows,
|
|
20
|
+
});
|
|
21
|
+
child.once('spawn', () => {
|
|
22
|
+
child.once('exit', (code) => {
|
|
23
|
+
if (code !== 0)
|
|
24
|
+
warnAboutBrowserFailure();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
await (0, node_events_1.once)(child, 'spawn');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
warnAboutBrowserFailure();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
exports.openUrlInBrowser = openUrlInBrowser;
|
package/package.json
CHANGED