@wirecat/cli-core 0.541.0 → 0.542.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/CHANGELOG.md +9 -0
- package/dist/oauth/authorize.js +21 -5
- package/dist/oauth/provider.d.ts +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ shape of this file.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.542.0 — 02.11.2026
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`redirect: { kind: "loopback", host: "localhost" }` signs in through `http://localhost:<port>/callback`.** Microsoft
|
|
15
|
+
Entra ignores the port only for `localhost` redirect URIs, so a public-client app registered as
|
|
16
|
+
`http://localhost/callback` needs it. The listener then takes `127.0.0.1` and, when the host has IPv6, `::1`, because
|
|
17
|
+
the browser may resolve `localhost` to either. Without `host` nothing changes: Google and the rest keep `127.0.0.1`.
|
|
18
|
+
|
|
10
19
|
## 0.541.0 — 02.11.2026
|
|
11
20
|
|
|
12
21
|
### Added
|
package/dist/oauth/authorize.js
CHANGED
|
@@ -46,7 +46,7 @@ export const authorize = async (options) => {
|
|
|
46
46
|
else
|
|
47
47
|
fail(new CliError("authentication_error", `${provider.name} sign-in was declined`, { reason: "oauth.declined" }));
|
|
48
48
|
};
|
|
49
|
-
const
|
|
49
|
+
const handler = (request, response) => {
|
|
50
50
|
let url;
|
|
51
51
|
try {
|
|
52
52
|
url = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
@@ -69,7 +69,11 @@ export const authorize = async (options) => {
|
|
|
69
69
|
}
|
|
70
70
|
response.end("Sign-in received; you can close this tab");
|
|
71
71
|
settle(url.searchParams);
|
|
72
|
-
}
|
|
72
|
+
};
|
|
73
|
+
const server = createServer(handler);
|
|
74
|
+
const localhost = provider.redirect.kind === "loopback" && provider.redirect.host === "localhost";
|
|
75
|
+
// `localhost` may resolve to ::1 in the browser; the IPv6 listener is best effort, as some hosts have no IPv6.
|
|
76
|
+
const server6 = localhost ? createServer(handler) : undefined;
|
|
73
77
|
const onAbort = () => fail(cancelled());
|
|
74
78
|
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
75
79
|
const timer = setTimeout(() => fail(new CliError("timeout", `${provider.name} sign-in timed out`, { reason: "oauth.timeout" })), options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
@@ -81,10 +85,19 @@ export const authorize = async (options) => {
|
|
|
81
85
|
const address = server.address();
|
|
82
86
|
if (!address || typeof address === "string")
|
|
83
87
|
throw new CliError("configuration_error", "could not start the local sign-in listener");
|
|
88
|
+
if (server6) {
|
|
89
|
+
server6.on("error", () => { });
|
|
90
|
+
await new Promise((resolve) => {
|
|
91
|
+
server6.once("error", () => resolve());
|
|
92
|
+
server6.listen(address.port, "::1", () => resolve());
|
|
93
|
+
});
|
|
94
|
+
}
|
|
84
95
|
const random = randomBytes(32).toString("base64url");
|
|
85
96
|
const relay = provider.redirect.kind === "relay";
|
|
86
97
|
state = relay ? `${address.port}.${random}` : random;
|
|
87
|
-
const redirectUri = provider.redirect.kind === "relay"
|
|
98
|
+
const redirectUri = provider.redirect.kind === "relay"
|
|
99
|
+
? provider.redirect.url
|
|
100
|
+
: `http://${localhost ? "localhost" : "127.0.0.1"}:${address.port}/callback`;
|
|
88
101
|
const url = new URL(provider.authorizeUrl);
|
|
89
102
|
for (const [key, value] of Object.entries({
|
|
90
103
|
...provider.authorizeParams,
|
|
@@ -124,7 +137,10 @@ export const authorize = async (options) => {
|
|
|
124
137
|
clearTimeout(timer);
|
|
125
138
|
options.signal?.removeEventListener("abort", onAbort);
|
|
126
139
|
paste.abort();
|
|
127
|
-
server
|
|
128
|
-
|
|
140
|
+
for (const each of server6 ? [server, server6] : [server]) {
|
|
141
|
+
each.closeAllConnections();
|
|
142
|
+
if (each.listening)
|
|
143
|
+
await new Promise((resolve) => each.close(() => resolve()));
|
|
144
|
+
}
|
|
129
145
|
}
|
|
130
146
|
};
|
package/dist/oauth/provider.d.ts
CHANGED
|
@@ -39,11 +39,13 @@ export interface OAuthProvider {
|
|
|
39
39
|
revokeUrl?: string;
|
|
40
40
|
client: OAuthClient;
|
|
41
41
|
/**
|
|
42
|
-
* `loopback` sends the browser to `http://127.0.0.1:<port>/callback
|
|
42
|
+
* `loopback` sends the browser to `http://127.0.0.1:<port>/callback`, or to `http://localhost:<port>/callback` with
|
|
43
|
+
* `host: "localhost"` (the listener then takes `127.0.0.1` and `::1`). `relay` sends it to a fixed page that
|
|
43
44
|
* forwards to the loopback port, which it reads from `state` (`<port>.<random>`).
|
|
44
45
|
*/
|
|
45
46
|
redirect: {
|
|
46
47
|
kind: "loopback";
|
|
48
|
+
host?: "localhost";
|
|
47
49
|
} | {
|
|
48
50
|
kind: "relay";
|
|
49
51
|
url: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.542.0",
|
|
4
4
|
"description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|