albert-heijn-mcp 1.6.0 → 1.7.0
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/README.md +31 -8
- package/dist/ahapi/client.js +8 -0
- package/dist/ahapi/client.js.map +1 -1
- package/dist/ahapi/member.js +4 -0
- package/dist/ahapi/member.js.map +1 -1
- package/dist/auth/browser.js +347 -0
- package/dist/auth/browser.js.map +1 -0
- package/dist/auth/login.js +17 -3
- package/dist/auth/login.js.map +1 -1
- package/dist/auth/session.js +87 -13
- package/dist/auth/session.js.map +1 -1
- package/dist/auth/tokens.js +8 -1
- package/dist/auth/tokens.js.map +1 -1
- package/dist/config.js +13 -3
- package/dist/config.js.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/loginCommand.js +74 -0
- package/dist/loginCommand.js.map +1 -0
- package/dist/tools/account.js +63 -73
- package/dist/tools/account.js.map +1 -1
- package/dist/tools/common.js +4 -18
- package/dist/tools/common.js.map +1 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -147,9 +147,15 @@ To install it permanently instead, run `npm install --global albert-heijn-mcp` a
|
|
|
147
147
|
|
|
148
148
|
## Logging in
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
Ask your assistant to log you in to Albert Heijn. You only do this once; the login is then kept and renewed automatically.
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
**On your computer** (Claude Desktop, Cursor, VS Code and other local apps), a separate browser window opens with AH's login page. Log in there as usual: the window closes by itself and you're logged in. Then tell your assistant you're done.
|
|
153
|
+
|
|
154
|
+
The window uses a fresh, empty browser profile, so type your password: saved passwords and password-manager extensions aren't available there. It needs Chrome, Edge, Brave or Chromium (not the snap version on Ubuntu) and a screen; otherwise you copy the code by hand as below.
|
|
155
|
+
|
|
156
|
+
**On a server** (ChatGPT, Claude.ai), or when no login window can open, you copy a code by hand. AH's login page has a captcha that only works on AH's own website, so the code has to come from your browser:
|
|
157
|
+
|
|
158
|
+
1. **Open the link** your assistant gives you and log in as usual.
|
|
153
159
|
2. **Copy the code back into the chat.** After you log in, AH tries to open its phone app, which your browser can't do, so the page seems stuck. The code you need is in the browser's developer console, a panel for web developers that you can open safely:
|
|
154
160
|
- Open it in Chrome with <kbd>⌘</kbd> <kbd>⌥</kbd> <kbd>J</kbd> on Mac, or <kbd>Ctrl</kbd> <kbd>Shift</kbd> <kbd>J</kbd> on Windows and Linux.
|
|
155
161
|
- Find this red line:
|
|
@@ -160,7 +166,7 @@ AH's login page has a captcha that only works on AH's own website, so logging in
|
|
|
160
166
|
|
|
161
167
|
- Copy the part from `appie://` up to the closing quote and paste it into the chat. The code works once and expires quickly, so paste it right away.
|
|
162
168
|
|
|
163
|
-
No line there? See [Troubleshooting](#troubleshooting).
|
|
169
|
+
No line there? See [Troubleshooting](#troubleshooting). To log a server in without the developer console, see [Running it on a server](#running-it-on-a-server).
|
|
164
170
|
|
|
165
171
|
<details>
|
|
166
172
|
<summary><b>Where your login is stored</b></summary>
|
|
@@ -178,6 +184,12 @@ Override the location with `AH_TOKENS_PATH`.
|
|
|
178
184
|
|
|
179
185
|
## Troubleshooting
|
|
180
186
|
|
|
187
|
+
<details>
|
|
188
|
+
<summary><b>No login window opens</b></summary>
|
|
189
|
+
|
|
190
|
+
The login window needs Chrome, Edge, Brave or Chromium installed in the usual place, and a screen: not over SSH, and not with Ubuntu's snap Chromium. Without one, your assistant gives you the link instead, and you [copy the code by hand](#logging-in). If the window closed before you finished, ask to log in again.
|
|
191
|
+
</details>
|
|
192
|
+
|
|
181
193
|
<details>
|
|
182
194
|
<summary><b>No "Failed to launch" line after logging in</b></summary>
|
|
183
195
|
|
|
@@ -285,6 +297,16 @@ It runs as a hardened systemd service behind a reverse proxy, installed from npm
|
|
|
285
297
|
}
|
|
286
298
|
```
|
|
287
299
|
|
|
300
|
+
To log the server in to Albert Heijn, ask a connected app to log you in and [copy the code by hand](#logging-in). Or skip the code: log in on your own computer with the login window, then copy the login to the server:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
AH_TOKENS_PATH=./server-tokens.json npx -y albert-heijn-mcp login
|
|
304
|
+
scp server-tokens.json your-server:ah-tokens.json && rm server-tokens.json
|
|
305
|
+
ssh -t your-server 'sudo sh -c "install -d -o albert-heijn-mcp -g albert-heijn-mcp -m 700 /home/albert-heijn-mcp/.config/albert-heijn-mcp && install -o albert-heijn-mcp -g albert-heijn-mcp -m 600 $HOME/ah-tokens.json /home/albert-heijn-mcp/.config/albert-heijn-mcp/tokens.json.new && mv /home/albert-heijn-mcp/.config/albert-heijn-mcp/tokens.json.new /home/albert-heijn-mcp/.config/albert-heijn-mcp/tokens.json && rm $HOME/ah-tokens.json"'
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The server picks it up on the next request; no restart needed. The separate file keeps your computer's own login apart: AH replaces a login every time it's renewed, so only one place can use each login. On a host without SSH, upload it to where the server keeps its login: `AH_TOKENS_PATH`, or the [default location](#logging-in) for the user it runs as.
|
|
309
|
+
|
|
288
310
|
The service can write only to `/home/albert-heijn-mcp`, where it keeps its tokens. If you point `AH_LOG_FILE` elsewhere, add that path to `ReadWritePaths` in the unit file.
|
|
289
311
|
|
|
290
312
|
## Configuration
|
|
@@ -293,7 +315,7 @@ Settings are environment variables. They can also go in a `.env` file in the wor
|
|
|
293
315
|
|
|
294
316
|
| Variable | Default | Description |
|
|
295
317
|
|---|---|---|
|
|
296
|
-
| `AH_REMOTE` | `false` | Don't open a browser on login (same as `--remote`). Always on with `streamable-http`. |
|
|
318
|
+
| `AH_REMOTE` | `false` | Don't open a browser on login; log in by copying the code instead (same as `--remote`). Always on with `streamable-http`. |
|
|
297
319
|
| `AH_TOKENS_PATH` | [per OS](#logging-in) | Where to store login tokens. |
|
|
298
320
|
| `AH_MCP_HOST` | `127.0.0.1` | Interface the HTTP server listens on. Keep the default behind a reverse proxy. |
|
|
299
321
|
| `AH_MCP_PORT` | `3000` | HTTP server port. |
|
|
@@ -305,9 +327,10 @@ Command-line flags:
|
|
|
305
327
|
|
|
306
328
|
```
|
|
307
329
|
node dist/index.js [--transport stdio|streamable-http] [--remote] [--version] [--help]
|
|
330
|
+
node dist/index.js login [--remote]
|
|
308
331
|
```
|
|
309
332
|
|
|
310
|
-
`stdio` (the default) is for local clients; `streamable-http` serves MCP at `/mcp`, with OAuth login at `/authorize`.
|
|
333
|
+
`stdio` (the default) is for local clients; `streamable-http` serves MCP at `/mcp`, with OAuth login at `/authorize`. `login` logs in to Albert Heijn from the terminal, saves the tokens and exits; with `--remote` it asks for the code instead of opening a login window.
|
|
311
334
|
|
|
312
335
|
## Tools
|
|
313
336
|
|
|
@@ -320,7 +343,7 @@ Tools that return data also return it as [structured output](https://modelcontex
|
|
|
320
343
|
|
|
321
344
|
| Tool | Description |
|
|
322
345
|
|---|---|
|
|
323
|
-
| `ah_login` | Log in: returns AH's login link
|
|
346
|
+
| `ah_login` | Log in: opens a login window on your computer, or returns AH's login link and completes the login with the code you paste back. |
|
|
324
347
|
| `ah_logout` | Delete the stored tokens, to switch accounts or reset a session. |
|
|
325
348
|
| `ah_get_member_profile` | Name, masked email, and bonus card number (last 4 digits). |
|
|
326
349
|
|
|
@@ -419,9 +442,9 @@ Run it from the checkout with `node dist/index.js`, or use `/path/to/albert-heij
|
|
|
419
442
|
|
|
420
443
|
| Path | Contents |
|
|
421
444
|
|---|---|
|
|
422
|
-
| [`src/index.ts`](src/index.ts), [`src/config.ts`](src/config.ts) | Entry point, flags and settings |
|
|
445
|
+
| [`src/index.ts`](src/index.ts), [`src/config.ts`](src/config.ts), [`src/loginCommand.ts`](src/loginCommand.ts) | Entry point, flags and settings, and the `login` command |
|
|
423
446
|
| [`src/ahapi/`](src/ahapi) | Client for AH's REST and GraphQL API, on Node's built-in `fetch` |
|
|
424
|
-
| [`src/auth/`](src/auth) | Login code exchange, token storage and refresh |
|
|
447
|
+
| [`src/auth/`](src/auth) | Login window, login code exchange, token storage and refresh |
|
|
425
448
|
| [`src/server/`](src/server) | Streamable HTTP transport and OAuth login |
|
|
426
449
|
| [`src/tools/`](src/tools) | The MCP tools, one file per area |
|
|
427
450
|
| [`deploy/`](deploy) | systemd unit, shipped in the package |
|
package/dist/ahapi/client.js
CHANGED
|
@@ -35,6 +35,14 @@ export class AhApiError extends Error {
|
|
|
35
35
|
export function hasStatus(err, status) {
|
|
36
36
|
return err instanceof AhApiError && err.status === status;
|
|
37
37
|
}
|
|
38
|
+
/** Reports whether err, or any error in its cause chain, is an HTTP 401 from AH. */
|
|
39
|
+
export function isUnauthorized(err) {
|
|
40
|
+
for (let e = err; e instanceof Error; e = e.cause) {
|
|
41
|
+
if (hasStatus(e, 401))
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
38
46
|
/** An error with our own context in front of its cause's message. */
|
|
39
47
|
export class ContextError extends Error {
|
|
40
48
|
/** Our text, without the cause's (which may quote AH). */
|
package/dist/ahapi/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/ahapi/client.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/ahapi/client.ts"],"names":[],"mappings":"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"}
|
package/dist/ahapi/member.js
CHANGED
package/dist/ahapi/member.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"member.js","sourceRoot":"","sources":["../../src/ahapi/member.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,CAAW;IACzC,MAAM,KAAK,GAAG;;;;;;;EAOd,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,OAAO,CAOlC,KAAK,CAAC,CAAC;IACV,OAAO;QACL,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QAC9B,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE;QAC5B,KAAK,EAAE,CAAC,CAAC,YAAY,IAAI,EAAE;QAC3B,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE;QACvC,eAAe,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;QACrC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;KACvD,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAA4C;IAC9D,OAAO,OAAO,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,CAAC,CAAC;AACxF,CAAC"}
|
|
1
|
+
{"version":3,"file":"member.js","sourceRoot":"","sources":["../../src/ahapi/member.ts"],"names":[],"mappings":"AAsBA,oCAAoC;AACpC,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,OAAO,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,CAAW;IACzC,MAAM,KAAK,GAAG;;;;;;;EAOd,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,OAAO,CAOlC,KAAK,CAAC,CAAC;IACV,OAAO;QACL,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QAC9B,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE;QAC5B,KAAK,EAAE,CAAC,CAAC,YAAY,IAAI,EAAE;QAC3B,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE;QACvC,eAAe,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;QACrC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;KACvD,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAA4C;IAC9D,OAAO,OAAO,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,CAAC,CAAC;AACxF,CAAC"}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdtemp, readdir, rm, stat } from "node:fs/promises";
|
|
4
|
+
import { homedir, tmpdir } from "node:os";
|
|
5
|
+
import { delimiter, join } from "node:path";
|
|
6
|
+
import { log } from "../log.js";
|
|
7
|
+
import { findCode } from "./login.js";
|
|
8
|
+
// Logging in without copying the code by hand: AH's login page ends by redirecting to the AH app's
|
|
9
|
+
// appie://login-exit?code=... link, which a browser can't open. A Chromium-based browser started with
|
|
10
|
+
// the DevTools protocol reports that redirect, so the code can be read from it.
|
|
11
|
+
/** How long the login window waits for the user. */
|
|
12
|
+
const LOGIN_TIMEOUT_MS = 10 * 60_000;
|
|
13
|
+
/** How long the browser gets to close before it is killed. */
|
|
14
|
+
const CLOSE_TIMEOUT_MS = 5000;
|
|
15
|
+
/** Temporary browser profiles are directories in the temp dir starting with this. */
|
|
16
|
+
const PROFILE_PREFIX = "albert-heijn-mcp-login-";
|
|
17
|
+
/** How long a new login window gets to fail before it counts as open. */
|
|
18
|
+
const START_CHECK_MS = 1500;
|
|
19
|
+
/** How long a failed login window is still worth mentioning. */
|
|
20
|
+
const ERROR_TTL_MS = 15 * 60_000;
|
|
21
|
+
/** Path of an installed Chromium-based browser (Chrome, Edge, Brave or Chromium) that can show a window. */
|
|
22
|
+
export function findBrowser() {
|
|
23
|
+
return browserCandidates().find((path) => existsSync(path));
|
|
24
|
+
}
|
|
25
|
+
function browserCandidates() {
|
|
26
|
+
switch (process.platform) {
|
|
27
|
+
case "darwin": {
|
|
28
|
+
// Over SSH there is no screen to show a window on.
|
|
29
|
+
if (process.env.SSH_CONNECTION)
|
|
30
|
+
return [];
|
|
31
|
+
const apps = ["Google Chrome", "Microsoft Edge", "Brave Browser", "Chromium"];
|
|
32
|
+
const dirs = ["/Applications", join(homedir(), "Applications")];
|
|
33
|
+
return apps.flatMap((app) => dirs.map((dir) => join(dir, `${app}.app`, "Contents", "MacOS", app)));
|
|
34
|
+
}
|
|
35
|
+
case "win32": {
|
|
36
|
+
if (process.env.SSH_CONNECTION)
|
|
37
|
+
return [];
|
|
38
|
+
const exes = [
|
|
39
|
+
["Google", "Chrome", "Application", "chrome.exe"],
|
|
40
|
+
["Microsoft", "Edge", "Application", "msedge.exe"],
|
|
41
|
+
["BraveSoftware", "Brave-Browser", "Application", "brave.exe"],
|
|
42
|
+
["Chromium", "Application", "chrome.exe"],
|
|
43
|
+
];
|
|
44
|
+
const roots = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env["PROGRAMFILES(X86)"]];
|
|
45
|
+
return exes.flatMap((exe) => roots.filter((root) => root !== undefined).map((root) => join(root, ...exe)));
|
|
46
|
+
}
|
|
47
|
+
default: {
|
|
48
|
+
// Without a display (a server, an SSH session) a window can't be shown.
|
|
49
|
+
if (!process.env.DISPLAY && !process.env.WAYLAND_DISPLAY)
|
|
50
|
+
return [];
|
|
51
|
+
// Snap browsers (Ubuntu's Chromium, also behind /usr/bin/chromium-browser) have a private /tmp,
|
|
52
|
+
// so their profile would land where it can't be removed.
|
|
53
|
+
const snapChromium = existsSync("/snap/bin/chromium");
|
|
54
|
+
const names = ["google-chrome", "google-chrome-stable", "microsoft-edge", "brave-browser"];
|
|
55
|
+
if (!snapChromium)
|
|
56
|
+
names.push("chromium", "chromium-browser");
|
|
57
|
+
const dirs = (process.env.PATH ?? "").split(delimiter).filter((dir) => dir && !dir.startsWith("/snap/"));
|
|
58
|
+
return names.flatMap((name) => dirs.map((dir) => join(dir, name)));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Opens url in the default browser; failures are ignored (the link is also returned). */
|
|
63
|
+
export function openInDefaultBrowser(url) {
|
|
64
|
+
const [cmd, args] = process.platform === "darwin"
|
|
65
|
+
? ["open", [url]]
|
|
66
|
+
: process.platform === "win32"
|
|
67
|
+
? ["rundll32", ["url.dll,FileProtocolHandler", url]]
|
|
68
|
+
: ["xdg-open", [url]];
|
|
69
|
+
try {
|
|
70
|
+
spawn(cmd, args, { stdio: "ignore", detached: true }).on("error", () => { }).unref();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Ignored.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** A login in a browser window that finishes in the background, e.g. while a tool call has returned. */
|
|
77
|
+
export class BrowserLogin {
|
|
78
|
+
#window;
|
|
79
|
+
/** Set while the code from the window is being exchanged and saved. */
|
|
80
|
+
#saving;
|
|
81
|
+
#failed;
|
|
82
|
+
/** Whether a login window is open and still waiting for the user. */
|
|
83
|
+
get running() {
|
|
84
|
+
return this.#window !== undefined && this.#saving === undefined;
|
|
85
|
+
}
|
|
86
|
+
/** Why the last login window didn't finish, if that was recent. */
|
|
87
|
+
get error() {
|
|
88
|
+
return this.#failed && Date.now() - this.#failed.at < ERROR_TTL_MS ? this.#failed.error : undefined;
|
|
89
|
+
}
|
|
90
|
+
/** Waits while a code from the window is being saved, so the login is stored once this returns. */
|
|
91
|
+
async saved() {
|
|
92
|
+
await this.#saving?.catch(() => { });
|
|
93
|
+
}
|
|
94
|
+
/** Closes the login window, if one is open, and waits until it has. */
|
|
95
|
+
async cancel() {
|
|
96
|
+
this.#window?.abort.abort();
|
|
97
|
+
await this.#window?.done;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Opens a login window at url; onCode runs with the code once the user has logged in. Returns false
|
|
101
|
+
* if no suitable browser is installed or the window failed right away (see error), e.g. no screen.
|
|
102
|
+
*/
|
|
103
|
+
async start(url, onCode) {
|
|
104
|
+
this.#failed = undefined;
|
|
105
|
+
const browser = findBrowser();
|
|
106
|
+
if (!browser)
|
|
107
|
+
return false;
|
|
108
|
+
// One window at a time; a code the old one already has is saved (or reported) first.
|
|
109
|
+
await this.saved();
|
|
110
|
+
this.#window?.abort.abort();
|
|
111
|
+
this.#saving = undefined;
|
|
112
|
+
const abort = new AbortController();
|
|
113
|
+
const fail = (err) => {
|
|
114
|
+
// A cancel is on purpose (e.g. the login was finished another way), not a failure.
|
|
115
|
+
if (abort.signal.aborted)
|
|
116
|
+
return;
|
|
117
|
+
log.warn("browser login failed", { err });
|
|
118
|
+
this.#failed = { error: err instanceof Error ? err : new Error(String(err)), at: Date.now() };
|
|
119
|
+
};
|
|
120
|
+
// Saving failures are reported right away, not only once the window has closed.
|
|
121
|
+
const save = (code) => (this.#saving = onCode(code).catch(fail));
|
|
122
|
+
const window = {
|
|
123
|
+
abort,
|
|
124
|
+
done: loginInWindow(url, browser, save, abort.signal)
|
|
125
|
+
.catch(fail)
|
|
126
|
+
.finally(() => {
|
|
127
|
+
// Only this window's state: a newer window may have started while this one closed.
|
|
128
|
+
if (this.#window !== window)
|
|
129
|
+
return;
|
|
130
|
+
this.#window = undefined;
|
|
131
|
+
this.#saving = undefined;
|
|
132
|
+
}),
|
|
133
|
+
};
|
|
134
|
+
this.#window = window;
|
|
135
|
+
// A browser that can't show a window usually exits at once.
|
|
136
|
+
let timer;
|
|
137
|
+
await Promise.race([window.done, new Promise((resolve) => (timer = setTimeout(resolve, START_CHECK_MS)))]);
|
|
138
|
+
clearTimeout(timer);
|
|
139
|
+
return this.#window === window && this.#failed === undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Opens url in a new window of browser, with a fresh profile; once the page redirects to the appie://
|
|
144
|
+
* link, calls use with the login code and resolves with its result. The window closes afterwards, so
|
|
145
|
+
* the short-lived code is used first. Rejects if the window is closed, times out or signal aborts first.
|
|
146
|
+
* The browser is controlled over a pipe, not a network port, so no other program can attach to it.
|
|
147
|
+
*/
|
|
148
|
+
export async function loginInWindow(url, browser, use, signal) {
|
|
149
|
+
await removeOldProfiles();
|
|
150
|
+
signal?.throwIfAborted();
|
|
151
|
+
const profile = await mkdtemp(join(tmpdir(), PROFILE_PREFIX));
|
|
152
|
+
if (signal?.aborted) {
|
|
153
|
+
await rm(profile, { recursive: true, force: true });
|
|
154
|
+
signal.throwIfAborted();
|
|
155
|
+
}
|
|
156
|
+
let child;
|
|
157
|
+
try {
|
|
158
|
+
child = spawn(browser, [`--user-data-dir=${profile}`, "--remote-debugging-pipe", "--no-first-run", "--no-default-browser-check", url],
|
|
159
|
+
// Its own process group, so Ctrl+C in a terminal reaches only this process, which then closes it.
|
|
160
|
+
{ stdio: ["ignore", "ignore", "ignore", "pipe", "pipe"], detached: process.platform !== "win32" });
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
await rm(profile, { recursive: true, force: true });
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
166
|
+
// "exit", not "close": a helper process may keep the pipes open after the browser has gone.
|
|
167
|
+
const exited = new Promise((resolve) => child.once("exit", () => resolve()));
|
|
168
|
+
const toBrowser = child.stdio[3];
|
|
169
|
+
const fromBrowser = child.stdio[4];
|
|
170
|
+
// No pipes if it couldn't start at all (e.g. too many open files).
|
|
171
|
+
if (!toBrowser || !fromBrowser) {
|
|
172
|
+
child.kill("SIGKILL");
|
|
173
|
+
await rm(profile, { recursive: true, force: true });
|
|
174
|
+
throw new Error("the browser could not be started");
|
|
175
|
+
}
|
|
176
|
+
// Errors also end the browser process, which ends the login.
|
|
177
|
+
toBrowser.on("error", () => { });
|
|
178
|
+
fromBrowser.on("error", () => { });
|
|
179
|
+
let lastId = 0;
|
|
180
|
+
/** Sends a command; returns its id. */
|
|
181
|
+
const send = (method, params = {}, sessionId) => {
|
|
182
|
+
toBrowser.write(`${JSON.stringify({ id: ++lastId, method, params, sessionId })}\0`);
|
|
183
|
+
return lastId;
|
|
184
|
+
};
|
|
185
|
+
// The code counts only when AH's login site sends the tab there: a navigation of a tab's main frame
|
|
186
|
+
// while it shows that site, or a redirect from it. Any other page could plant someone else's code.
|
|
187
|
+
const mainFrames = new Map();
|
|
188
|
+
// On macOS the browser keeps running after its last window closes, so watch the tabs instead.
|
|
189
|
+
const pages = new Set();
|
|
190
|
+
let onAllClosed = () => { };
|
|
191
|
+
const frameTreeRequests = new Map();
|
|
192
|
+
const onMessage = (raw) => {
|
|
193
|
+
// Most messages are network events; parse only the few that can matter.
|
|
194
|
+
if (!/appie:\/\/login-exit\?|"method":"(Target\.|Page\.frameNavigated)|"frameTree"/.test(raw))
|
|
195
|
+
return undefined;
|
|
196
|
+
const msg = JSON.parse(raw);
|
|
197
|
+
const p = msg.params ?? {};
|
|
198
|
+
const sessionId = msg.sessionId ?? (msg.id !== undefined ? frameTreeRequests.get(msg.id) : undefined);
|
|
199
|
+
const frame = p.frame ?? msg.result?.frameTree?.frame;
|
|
200
|
+
if (frame && !frame.parentId && sessionId)
|
|
201
|
+
mainFrames.set(sessionId, { id: frame.id, url: frame.url });
|
|
202
|
+
switch (msg.method) {
|
|
203
|
+
case "Target.targetCreated":
|
|
204
|
+
if (p.targetInfo?.type === "page") {
|
|
205
|
+
pages.add(p.targetInfo.targetId);
|
|
206
|
+
send("Target.attachToTarget", { targetId: p.targetInfo.targetId, flatten: true });
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
case "Target.targetDestroyed":
|
|
210
|
+
if (p.targetId && pages.delete(p.targetId) && pages.size === 0)
|
|
211
|
+
onAllClosed();
|
|
212
|
+
break;
|
|
213
|
+
case "Target.attachedToTarget":
|
|
214
|
+
if (p.sessionId) {
|
|
215
|
+
send("Page.enable", {}, p.sessionId);
|
|
216
|
+
// The page may have loaded before Page.enable, so ask which one it shows.
|
|
217
|
+
frameTreeRequests.set(send("Page.getFrameTree", {}, p.sessionId), p.sessionId);
|
|
218
|
+
// No response bodies are needed, so Chrome needn't keep them.
|
|
219
|
+
send("Network.enable", { maxTotalBufferSize: 0, maxResourceBufferSize: 0 }, p.sessionId);
|
|
220
|
+
}
|
|
221
|
+
break;
|
|
222
|
+
case "Page.frameRequestedNavigation":
|
|
223
|
+
case "Page.frameScheduledNavigation": {
|
|
224
|
+
const main = msg.sessionId ? mainFrames.get(msg.sessionId) : undefined;
|
|
225
|
+
if (main && p.frameId === main.id && isAhPage(main.url))
|
|
226
|
+
return redirectCode(p.url);
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
case "Network.requestWillBeSent": {
|
|
230
|
+
// Only the tab's own page: an embedded frame or image could be sent there through an ah.nl redirect.
|
|
231
|
+
const main = msg.sessionId ? mainFrames.get(msg.sessionId) : undefined;
|
|
232
|
+
if (p.type === "Document" && main && p.frameId === main.id && p.redirectResponse && isAhPage(p.redirectResponse.url)) {
|
|
233
|
+
return redirectCode(p.request?.url);
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
};
|
|
240
|
+
let timer;
|
|
241
|
+
try {
|
|
242
|
+
const code = await new Promise((resolve, reject) => {
|
|
243
|
+
timer = setTimeout(() => reject(new Error("the login window timed out")), LOGIN_TIMEOUT_MS);
|
|
244
|
+
const closed = () => reject(new Error("the login window was closed before the login finished"));
|
|
245
|
+
void exited.then(closed);
|
|
246
|
+
onAllClosed = closed;
|
|
247
|
+
const cancelled = () => reject(new Error("the login was cancelled"));
|
|
248
|
+
if (signal?.aborted)
|
|
249
|
+
cancelled();
|
|
250
|
+
signal?.addEventListener("abort", cancelled, { once: true });
|
|
251
|
+
child.once("error", reject);
|
|
252
|
+
readMessages(fromBrowser, (raw) => {
|
|
253
|
+
try {
|
|
254
|
+
const code = onMessage(raw);
|
|
255
|
+
if (code)
|
|
256
|
+
resolve(code);
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// Not a message we can use.
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
// Also reports the tabs that are already open.
|
|
263
|
+
send("Target.setDiscoverTargets", { discover: true });
|
|
264
|
+
});
|
|
265
|
+
return await use(code);
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
clearTimeout(timer);
|
|
269
|
+
fromBrowser.removeAllListeners("data");
|
|
270
|
+
// Not if it failed to start or has already exited.
|
|
271
|
+
if (child.pid !== undefined && child.exitCode === null && child.signalCode === null) {
|
|
272
|
+
send("Browser.close");
|
|
273
|
+
const killTimer = setTimeout(() => killBrowser(child), CLOSE_TIMEOUT_MS);
|
|
274
|
+
await exited;
|
|
275
|
+
clearTimeout(killTimer);
|
|
276
|
+
}
|
|
277
|
+
// A helper process may still hold the pipes; they'd keep this process running.
|
|
278
|
+
toBrowser.destroy();
|
|
279
|
+
fromBrowser.destroy();
|
|
280
|
+
await rm(profile, { recursive: true, force: true, maxRetries: 5 }).catch(() => { });
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/** Kills the browser and, where it has its own process group, its helper processes too. */
|
|
284
|
+
function killBrowser(child) {
|
|
285
|
+
try {
|
|
286
|
+
if (process.platform !== "win32" && child.pid !== undefined)
|
|
287
|
+
process.kill(-child.pid, "SIGKILL");
|
|
288
|
+
else
|
|
289
|
+
child.kill("SIGKILL");
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
child.kill("SIGKILL");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Removes temporary profiles left behind by a process that was killed during a login: they may hold
|
|
297
|
+
* AH cookies. Only ones older than any login window can be, so a window open elsewhere is left alone.
|
|
298
|
+
*/
|
|
299
|
+
async function removeOldProfiles() {
|
|
300
|
+
// Twice the timeout: saving the code can still take a while after the window got it.
|
|
301
|
+
const cutoff = Date.now() - 2 * LOGIN_TIMEOUT_MS;
|
|
302
|
+
let names;
|
|
303
|
+
try {
|
|
304
|
+
names = await readdir(tmpdir());
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
for (const name of names.filter((n) => n.startsWith(PROFILE_PREFIX))) {
|
|
310
|
+
const path = join(tmpdir(), name);
|
|
311
|
+
try {
|
|
312
|
+
if ((await stat(path)).mtimeMs < cutoff)
|
|
313
|
+
await rm(path, { recursive: true, force: true });
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
// E.g. another user's, or already gone; the others still get removed.
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/** Calls onMessage with each message from the browser: JSON text, each ending in a NUL byte. */
|
|
321
|
+
function readMessages(stream, onMessage) {
|
|
322
|
+
let buffered = "";
|
|
323
|
+
stream.setEncoding("utf8");
|
|
324
|
+
stream.on("data", (chunk) => {
|
|
325
|
+
buffered += chunk;
|
|
326
|
+
let start = 0;
|
|
327
|
+
for (let end = buffered.indexOf("\0"); end >= 0; end = buffered.indexOf("\0", start)) {
|
|
328
|
+
onMessage(buffered.slice(start, end));
|
|
329
|
+
start = end + 1;
|
|
330
|
+
}
|
|
331
|
+
buffered = buffered.slice(start);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/** The login code if url is the appie://login-exit link itself, not merely mentions it. */
|
|
335
|
+
function redirectCode(url) {
|
|
336
|
+
return url?.startsWith("appie://login-exit?") ? findCode(url) : undefined;
|
|
337
|
+
}
|
|
338
|
+
/** Reports whether url is a page of AH's login site, which sends the browser to the appie:// link. */
|
|
339
|
+
function isAhPage(url) {
|
|
340
|
+
try {
|
|
341
|
+
return new URL(url).hostname === "login.ah.nl";
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/auth/browser.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/login.js
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
|
+
import { LOGIN_URL } from "../ahapi/index.js";
|
|
1
2
|
/** code=... in the pasted appie://login-exit link or the console line quoting it; links elsewhere don't count. */
|
|
2
3
|
const CODE_PARAM = /appie:\/\/login-exit\?(?:[^\s'"]*&)?code=([A-Za-z0-9._~-]+)/;
|
|
3
4
|
/** A bare code. */
|
|
4
5
|
const BARE_CODE = /^[A-Za-z0-9._~-]{8,}$/;
|
|
6
|
+
/** The login code in an appie://login-exit link anywhere in text, if there is one. */
|
|
7
|
+
export function findCode(text) {
|
|
8
|
+
return CODE_PARAM.exec(text)?.[1];
|
|
9
|
+
}
|
|
5
10
|
/** Extracts the login code from the pasted redirect link, console line, or bare code. */
|
|
6
11
|
export function extractCode(input) {
|
|
7
12
|
const trimmed = input.trim();
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
return
|
|
13
|
+
const code = findCode(trimmed);
|
|
14
|
+
if (code)
|
|
15
|
+
return code;
|
|
11
16
|
if (BARE_CODE.test(trimmed))
|
|
12
17
|
return trimmed;
|
|
13
18
|
throw new Error("no login code found: paste the appie://login-exit?code=... link or the code itself");
|
|
14
19
|
}
|
|
20
|
+
/** The steps for logging in by copying the code by hand; paste says where step 3 pastes it. */
|
|
21
|
+
export function manualLoginSteps(opened, paste) {
|
|
22
|
+
return `1. Open this link${opened ? " (it should have opened in your browser)" : ""} and log in:
|
|
23
|
+
${LOGIN_URL}
|
|
24
|
+
2. After logging in, the page stays put: the browser cannot open the AH app link it redirects to.
|
|
25
|
+
Open the developer console (Chrome: Cmd+Option+J on Mac, Ctrl+Shift+J on Windows/Linux)
|
|
26
|
+
and copy the link from the "Failed to launch 'appie://login-exit?code=...'" message.
|
|
27
|
+
3. Paste that link ${paste}. The code is single-use and expires quickly, so do this right away.`;
|
|
28
|
+
}
|
|
15
29
|
//# sourceMappingURL=login.js.map
|
package/dist/auth/login.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"AAAA,kHAAkH;AAClH,MAAM,UAAU,GAAG,6DAA6D,CAAC;AACjF,mBAAmB;AACnB,MAAM,SAAS,GAAG,uBAAuB,CAAC;AAE1C,yFAAyF;AACzF,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,kHAAkH;AAClH,MAAM,UAAU,GAAG,6DAA6D,CAAC;AACjF,mBAAmB;AACnB,MAAM,SAAS,GAAG,uBAAuB,CAAC;AAE1C,sFAAsF;AACtF,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC5C,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC,CAAC;AACxG,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,gBAAgB,CAAC,MAAe,EAAE,KAAa;IAC7D,OAAO,oBAAoB,MAAM,CAAC,CAAC,CAAC,0CAA0C,CAAC,CAAC,CAAC,EAAE;KAChF,SAAS;;;;qBAIO,KAAK,sEAAsE,CAAC;AACjG,CAAC"}
|