ag-awsauth 0.0.292 → 0.0.294
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/helpers/browser.d.ts +3 -1
- package/dist/helpers/browser.js +173 -18
- package/package.json +12 -9
|
@@ -2,7 +2,9 @@ import type { Page } from 'puppeteer';
|
|
|
2
2
|
export declare const closeBrowser: () => Promise<void>;
|
|
3
3
|
export declare const launchBrowser: () => Promise<void>;
|
|
4
4
|
export declare const goToPage: (url: string) => Promise<Page>;
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const allowAccessSelectors: string[];
|
|
6
|
+
export declare const dismissAllowAccessPrompt: (page: Page, perSelectorTimeout?: number) => Promise<boolean>;
|
|
7
|
+
export declare const allowAccessIfRequested: (page: Page, timeout?: number) => Promise<boolean>;
|
|
6
8
|
export declare function getMFA(p: {
|
|
7
9
|
verificationUriComplete: string;
|
|
8
10
|
creds: {
|
package/dist/helpers/browser.js
CHANGED
|
@@ -9,9 +9,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.allowAccessIfRequested = exports.goToPage = exports.launchBrowser = exports.closeBrowser = void 0;
|
|
12
|
+
exports.allowAccessIfRequested = exports.dismissAllowAccessPrompt = exports.allowAccessSelectors = exports.goToPage = exports.launchBrowser = exports.closeBrowser = void 0;
|
|
13
13
|
exports.getMFA = getMFA;
|
|
14
14
|
const log_1 = require("ag-common/dist/common/helpers/log");
|
|
15
|
+
const sleep_1 = require("ag-common/dist/common/helpers/sleep");
|
|
15
16
|
const puppeteer_1 = require("puppeteer");
|
|
16
17
|
const __1 = require("..");
|
|
17
18
|
const config_1 = require("../config");
|
|
@@ -83,15 +84,173 @@ const goToPage = (url) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
83
84
|
}
|
|
84
85
|
});
|
|
85
86
|
exports.goToPage = goToPage;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
exports.allowAccessSelectors = [
|
|
88
|
+
'[data-testid="allow-access-button"]',
|
|
89
|
+
'[data-analytics="allow-access-button"]',
|
|
90
|
+
'button[data-testid="allow-access-button"]',
|
|
91
|
+
];
|
|
92
|
+
const clickSelectorIfPresent = (page_1, selector_1, ...args_1) => __awaiter(void 0, [page_1, selector_1, ...args_1], void 0, function* (page, selector, timeout = config_1.timeoutShortMs) {
|
|
93
|
+
try {
|
|
94
|
+
const el = yield page.waitForSelector(selector, {
|
|
95
|
+
timeout,
|
|
96
|
+
visible: true,
|
|
97
|
+
});
|
|
98
|
+
if (!el) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
yield el.click();
|
|
103
|
+
}
|
|
104
|
+
catch (_a) {
|
|
105
|
+
// Element may detach between query and click (SPA re-render).
|
|
106
|
+
// Fall back to an in-page click which re-queries the selector.
|
|
107
|
+
yield page.$eval(selector, (bn) => bn.click()).catch(() => undefined);
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch (_b) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const clickAllowAccessByText = (page) => {
|
|
116
|
+
try {
|
|
117
|
+
const clicked = page.evaluate(() => {
|
|
118
|
+
const isVisible = (el) => {
|
|
119
|
+
const rect = el.getBoundingClientRect();
|
|
120
|
+
return rect.width > 0 && rect.height > 0 && getComputedStyle(el).visibility !== 'hidden';
|
|
121
|
+
};
|
|
122
|
+
// Match button, link, or ARIA-button variants: AWS renders the
|
|
123
|
+
// "Allow access" consent action differently across releases
|
|
124
|
+
// (orange button vs link), so don't assume a <button>.
|
|
125
|
+
const candidates = Array.from(document.querySelectorAll('button, a, [role="button"], input[type="submit"], input[type="button"]')).filter((el) => isVisible(el));
|
|
126
|
+
const label = (el) => {
|
|
127
|
+
var _a;
|
|
128
|
+
const inputValue = el.value;
|
|
129
|
+
// oxlint-disable-next-line typescript/no-unnecessary-condition -- value is undefined on non-inputs
|
|
130
|
+
const text = typeof inputValue === 'string' ? inputValue : ((_a = el.textContent) !== null && _a !== void 0 ? _a : '');
|
|
131
|
+
return text.trim().toLowerCase();
|
|
132
|
+
};
|
|
133
|
+
const exact = candidates.find((el) => label(el) === 'allow access');
|
|
134
|
+
const target = exact !== null && exact !== void 0 ? exact : candidates.find((el) => label(el).includes('allow access'));
|
|
135
|
+
if (target) {
|
|
136
|
+
target.click();
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
});
|
|
141
|
+
return clicked;
|
|
142
|
+
}
|
|
143
|
+
catch (_a) {
|
|
144
|
+
return Promise.resolve(false);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const dismissAllowAccessPrompt = (page_1, ...args_1) => __awaiter(void 0, [page_1, ...args_1], void 0, function* (page, perSelectorTimeout = 1000) {
|
|
148
|
+
const clicked = yield Promise.all(exports.allowAccessSelectors.map((selector) => clickSelectorIfPresent(page, selector, perSelectorTimeout)));
|
|
149
|
+
if (clicked.some(Boolean)) {
|
|
150
|
+
(0, log_1.info)('click allow-access-button');
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (yield clickAllowAccessByText(page)) {
|
|
154
|
+
(0, log_1.info)('click allow-access-button (text match "Allow access")');
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
});
|
|
159
|
+
exports.dismissAllowAccessPrompt = dismissAllowAccessPrompt;
|
|
160
|
+
const pollDelayMs = 500;
|
|
161
|
+
const sleepUntilDeadline = (deadline) => __awaiter(void 0, void 0, void 0, function* () {
|
|
162
|
+
const remaining = deadline - Date.now();
|
|
163
|
+
if (remaining <= 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
yield (0, sleep_1.sleep)(Math.min(pollDelayMs, remaining));
|
|
167
|
+
});
|
|
168
|
+
const allowAccessIfRequested = (page_1, ...args_1) => __awaiter(void 0, [page_1, ...args_1], void 0, function* (page, timeout = config_1.timeoutMs) {
|
|
169
|
+
const deadline = Date.now() + timeout;
|
|
170
|
+
// Poll so a slow-rendering consent dialog is not missed, while still
|
|
171
|
+
// returning quickly when the prompt appears (or never appears).
|
|
172
|
+
// oxlint-disable-next-line no-await-in-loop -- polling must stay sequential to match dialog timing
|
|
173
|
+
for (;;) {
|
|
174
|
+
// oxlint-disable-next-line no-await-in-loop -- each poll depends on the current page state
|
|
175
|
+
if (yield (0, exports.dismissAllowAccessPrompt)(page)) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
if (Date.now() >= deadline) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
// oxlint-disable-next-line no-await-in-loop -- delay must elapse before the next poll
|
|
182
|
+
yield sleepUntilDeadline(deadline);
|
|
90
183
|
}
|
|
91
|
-
(0, log_1.info)('click allow-access-button');
|
|
92
|
-
yield allowAccess.click();
|
|
93
184
|
});
|
|
94
185
|
exports.allowAccessIfRequested = allowAccessIfRequested;
|
|
186
|
+
const waitForSuccessDefensively = (page_1, ...args_1) => __awaiter(void 0, [page_1, ...args_1], void 0, function* (page, timeout = config_1.timeoutMs) {
|
|
187
|
+
const deadline = Date.now() + timeout;
|
|
188
|
+
let lastError;
|
|
189
|
+
// oxlint-disable-next-line no-await-in-loop -- success and consent checks are order-dependent
|
|
190
|
+
for (;;) {
|
|
191
|
+
// Success already visible?
|
|
192
|
+
try {
|
|
193
|
+
// oxlint-disable-next-line no-await-in-loop -- must check before dismissing prompts
|
|
194
|
+
const success = yield page.waitForSelector('[data-analytics-alert="success"]', { timeout: 2000 });
|
|
195
|
+
if (success) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
lastError = e;
|
|
201
|
+
}
|
|
202
|
+
// Defensively dismiss the consent prompt if it is (still) showing.
|
|
203
|
+
// This covers the "Allow <user> to access your data? / Allow access"
|
|
204
|
+
// dialog which otherwise blocks success indefinitely.
|
|
205
|
+
try {
|
|
206
|
+
// oxlint-disable-next-line no-await-in-loop -- prompt check follows the success check
|
|
207
|
+
if (yield (0, exports.dismissAllowAccessPrompt)(page)) {
|
|
208
|
+
(0, log_1.info)('dismissed allow-access prompt while waiting for success');
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
(0, log_1.debug)('error dismissing allow-access prompt:', e);
|
|
214
|
+
}
|
|
215
|
+
// Also re-check the other intermediate confirmations while polling.
|
|
216
|
+
try {
|
|
217
|
+
// oxlint-disable-next-line no-await-in-loop -- confirmation check follows the success check
|
|
218
|
+
if (yield clickSelectorIfPresent(page, '[data-analytics="accept-user-code"]', 1000)) {
|
|
219
|
+
(0, log_1.info)('click accept-user-code (while waiting for success)');
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch (e) {
|
|
224
|
+
(0, log_1.debug)('error clicking accept-user-code while waiting:', e);
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
// oxlint-disable-next-line no-await-in-loop -- auth request check follows the success check
|
|
228
|
+
if (yield clickSelectorIfPresent(page, '#cli_verification_btn', 1000)) {
|
|
229
|
+
(0, log_1.info)('clicking auth request button (while waiting for success)');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
(0, log_1.debug)('error clicking auth request button while waiting:', e);
|
|
234
|
+
}
|
|
235
|
+
if (Date.now() >= deadline) {
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
// oxlint-disable-next-line no-await-in-loop -- delay must elapse before the next poll
|
|
239
|
+
yield sleepUntilDeadline(deadline);
|
|
240
|
+
}
|
|
241
|
+
// Final wait to surface a clear TimeoutError if success never arrived.
|
|
242
|
+
try {
|
|
243
|
+
yield page.waitForSelector('[data-analytics-alert="success"]', {
|
|
244
|
+
timeout: config_1.timeoutShortMs,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
catch (_a) {
|
|
248
|
+
if (lastError instanceof Error) {
|
|
249
|
+
throw lastError;
|
|
250
|
+
}
|
|
251
|
+
throw new Error('Timed out waiting for success');
|
|
252
|
+
}
|
|
253
|
+
});
|
|
95
254
|
function getMFA(p) {
|
|
96
255
|
return __awaiter(this, void 0, void 0, function* () {
|
|
97
256
|
var _a;
|
|
@@ -203,15 +362,11 @@ function getMFA(p) {
|
|
|
203
362
|
}
|
|
204
363
|
try {
|
|
205
364
|
(0, log_1.info)('accept-user-code');
|
|
206
|
-
|
|
207
|
-
timeout: config_1.timeoutMs,
|
|
208
|
-
});
|
|
209
|
-
if (messageDiv) {
|
|
365
|
+
if (yield clickSelectorIfPresent(page, '[data-analytics="accept-user-code"]', config_1.timeoutMs)) {
|
|
210
366
|
(0, log_1.info)('click accept-user-code');
|
|
211
|
-
yield messageDiv.click();
|
|
212
367
|
}
|
|
213
368
|
else {
|
|
214
|
-
|
|
369
|
+
(0, log_1.debug)('accept-user-code prompt not present, continuing');
|
|
215
370
|
}
|
|
216
371
|
}
|
|
217
372
|
catch (e) {
|
|
@@ -220,8 +375,10 @@ function getMFA(p) {
|
|
|
220
375
|
let accessStateFound = false;
|
|
221
376
|
try {
|
|
222
377
|
(0, log_1.info)('allow-access-button');
|
|
223
|
-
yield (0, exports.allowAccessIfRequested)(page);
|
|
224
|
-
accessStateFound
|
|
378
|
+
accessStateFound = yield (0, exports.allowAccessIfRequested)(page, config_1.timeoutMedMs);
|
|
379
|
+
if (!accessStateFound) {
|
|
380
|
+
(0, log_1.debug)('allow-access prompt not present, continuing');
|
|
381
|
+
}
|
|
225
382
|
}
|
|
226
383
|
catch (e) {
|
|
227
384
|
(0, log_1.debug)('error clicking allow-access-button:', e);
|
|
@@ -242,9 +399,7 @@ function getMFA(p) {
|
|
|
242
399
|
}
|
|
243
400
|
//
|
|
244
401
|
(0, log_1.info)('waiting for success');
|
|
245
|
-
yield page
|
|
246
|
-
timeout: config_1.timeoutMs,
|
|
247
|
-
});
|
|
402
|
+
yield waitForSuccessDefensively(page, config_1.timeoutMs);
|
|
248
403
|
(0, log_1.warn)('mfa success');
|
|
249
404
|
const cookies = yield page.cookies();
|
|
250
405
|
const ssoAuthn = (_a = cookies.find((c) => c.name === 'x-amz-sso_authn')) === null || _a === void 0 ? void 0 : _a.value;
|
package/package.json
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ag-awsauth",
|
|
3
|
+
"version": "0.0.294",
|
|
4
|
+
"private": false,
|
|
3
5
|
"description": "auth to aws sso/iamv2 easily",
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"author": "andreigec@hotmail.com",
|
|
6
6
|
"license": "ISC",
|
|
7
|
-
"
|
|
8
|
-
"version": "0.0.292",
|
|
9
|
-
"preferGlobal": true,
|
|
7
|
+
"author": "andreigec@hotmail.com",
|
|
10
8
|
"bin": {
|
|
11
9
|
"ag-awsauth": "./bin/awsauth.js"
|
|
12
10
|
},
|
|
@@ -16,18 +14,21 @@
|
|
|
16
14
|
"README.md",
|
|
17
15
|
"LICENSE.md"
|
|
18
16
|
],
|
|
17
|
+
"main": "dist/index.js",
|
|
19
18
|
"dependencies": {
|
|
20
19
|
"@aws-sdk/client-sso": "3.972.0",
|
|
21
20
|
"@aws-sdk/client-sso-oidc": "3.972.0",
|
|
22
21
|
"@aws-sdk/client-sts": "3.972.0",
|
|
23
22
|
"@aws-sdk/shared-ini-file-loader": "3.374.0",
|
|
24
|
-
"ag-common": "0.0.
|
|
23
|
+
"ag-common": "0.0.911",
|
|
25
24
|
"cli-select": "1.1.2",
|
|
26
25
|
"dotenv": "17.2.3",
|
|
27
26
|
"envfile": "7.1.0",
|
|
28
|
-
"eslint-config-e7npm": "0.1.
|
|
27
|
+
"eslint-config-e7npm": "0.1.68",
|
|
29
28
|
"ini": "5.0.0",
|
|
30
29
|
"node-fetch": "3.3.2",
|
|
30
|
+
"oxfmt": "^0.65.0",
|
|
31
|
+
"oxlint": "^1.83.0",
|
|
31
32
|
"puppeteer": "24.35.0",
|
|
32
33
|
"readline-sync": "1.4.10",
|
|
33
34
|
"typescript": "5.9.3",
|
|
@@ -43,12 +44,14 @@
|
|
|
43
44
|
"resolutions": {
|
|
44
45
|
"ws": ">=8.17.1"
|
|
45
46
|
},
|
|
47
|
+
"preferGlobal": true,
|
|
46
48
|
"engines": {
|
|
47
49
|
"node": ">=20"
|
|
48
50
|
},
|
|
49
51
|
"scripts": {
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
+
"format": "e7-oxfmt --config .oxfmtrc.json --disable-nested-config .",
|
|
53
|
+
"lint": "e7-oxlint --config .oxlintrc.json --deny-warnings src && pnpm run typecheck",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
52
55
|
"test": "tsc && node --test test/*.test.js",
|
|
53
56
|
"start": "tsc && node bin/awsauth.js",
|
|
54
57
|
"build": "tsc"
|