@deque/axe-auth 1.1.0-next.d59ba863 → 1.1.0-next.d5a755f8
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/credits.json +11 -0
- package/dist/cli/commonArgs.d.ts +11 -58
- package/dist/cli/commonArgs.js +8 -35
- package/dist/cli/confirm.js +0 -3
- package/dist/cli/errors.d.ts +2 -9
- package/dist/cli/errors.js +2 -9
- package/dist/cli/safeExit.d.ts +8 -0
- package/dist/cli/safeExit.js +16 -0
- package/dist/cli/types.d.ts +10 -50
- package/dist/commands/login.d.ts +1 -4
- package/dist/commands/login.js +5 -14
- package/dist/commands/logout.js +0 -2
- package/dist/commands/token.js +0 -4
- package/dist/index.js +8 -14
- package/dist/oauth/authorizationURL.d.ts +2 -7
- package/dist/oauth/authorizationURL.js +3 -7
- package/dist/oauth/authorize.d.ts +13 -51
- package/dist/oauth/authorize.js +8 -10
- package/dist/oauth/callbackServer.d.ts +20 -9
- package/dist/oauth/callbackServer.js +33 -27
- package/dist/oauth/discoverOIDC.d.ts +10 -27
- package/dist/oauth/discoverOIDC.js +17 -46
- package/dist/oauth/discoverSSOConfig.d.ts +2 -12
- package/dist/oauth/errors.d.ts +2 -0
- package/dist/oauth/getValidAccessToken.d.ts +9 -44
- package/dist/oauth/getValidAccessToken.js +7 -16
- package/dist/oauth/openBrowser.d.ts +14 -3
- package/dist/oauth/openBrowser.js +23 -6
- package/dist/oauth/refreshTokens.js +3 -5
- package/dist/oauth/renderHTML.d.ts +12 -0
- package/dist/oauth/{renderHtml.js → renderHTML.js} +17 -11
- package/dist/oauth/retry.d.ts +2 -0
- package/dist/oauth/retry.js +50 -0
- package/dist/oauth/revokeToken.js +3 -2
- package/dist/oauth/tokenExchange.d.ts +1 -1
- package/dist/oauth/tokenExchange.js +4 -3
- package/dist/oauth/tokenResponse.d.ts +6 -38
- package/dist/oauth/tokenResponse.js +7 -27
- package/dist/oauth/tokenStore.d.ts +58 -3
- package/dist/oauth/tokenStore.js +374 -31
- package/docs/callback-page.md +2 -2
- package/docs/callback-server.md +1 -1
- package/package.json +16 -5
- package/dist/oauth/renderHtml.d.ts +0 -9
package/credits.json
CHANGED
|
@@ -27,6 +27,17 @@
|
|
|
27
27
|
"publisher": "Stephen Mathieson",
|
|
28
28
|
"email": "me@stephenmathieson.com"
|
|
29
29
|
},
|
|
30
|
+
"shlex@3.0.0": {
|
|
31
|
+
"name": "shlex",
|
|
32
|
+
"version": "3.0.0",
|
|
33
|
+
"licenses": "MIT",
|
|
34
|
+
"path": "/home/runner/work/axe-mcp-server/axe-mcp-server/node_modules/.pnpm/shlex@3.0.0/node_modules/shlex",
|
|
35
|
+
"licenseText": "MIT License\n\nCopyright (c) 2018 Ryan Govostes\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
|
|
36
|
+
"licenseFile": "/home/runner/work/axe-mcp-server/axe-mcp-server/node_modules/.pnpm/shlex@3.0.0/node_modules/shlex/LICENSE",
|
|
37
|
+
"repository": "https://github.com/rgov/node-shlex",
|
|
38
|
+
"publisher": "Ryan Govostes",
|
|
39
|
+
"copyright": "Copyright (c) 2018 Ryan Govostes"
|
|
40
|
+
},
|
|
30
41
|
"ts-dedent@2.2.0": {
|
|
31
42
|
"name": "ts-dedent",
|
|
32
43
|
"version": "2.2.0",
|
package/dist/cli/commonArgs.d.ts
CHANGED
|
@@ -1,37 +1,18 @@
|
|
|
1
1
|
import type { ParseArgsConfig } from "node:util";
|
|
2
|
-
/**
|
|
3
|
-
* Default axe server URL for `axe-auth` users on Deque's SaaS prod
|
|
4
|
-
* deployment. The CLI's `--server` flag (and `AXE_SERVER_URL` env)
|
|
5
|
-
* override this; non-prod customers must supply their own walnut URL.
|
|
6
|
-
*/
|
|
2
|
+
/** Default axe server URL for Deque SaaS prod. */
|
|
7
3
|
export declare const DEFAULT_WALNUT_URL = "https://axe.deque.com";
|
|
8
4
|
/** Common configuration the CLI verbs share. */
|
|
9
5
|
export interface CommonArgs {
|
|
10
|
-
/**
|
|
11
|
-
* axe server URL (walnut). Used by `login` to fetch
|
|
12
|
-
* `/api/sso-config` and derive the OAuth issuer / realm / client
|
|
13
|
-
* coordinates. `token` and `logout` operate on the stored entry
|
|
14
|
-
* alone and ignore this value.
|
|
15
|
-
*/
|
|
6
|
+
/** axe server URL (walnut). */
|
|
16
7
|
walnutURL: string;
|
|
17
|
-
/**
|
|
18
|
-
* Whether to permit non-loopback http walnut/issuer URLs. Loopback
|
|
19
|
-
* hosts (`localhost` / `127.0.0.1` / `[::1]`) are always allowed
|
|
20
|
-
* over http; this flag is the opt-in for non-loopback http
|
|
21
|
-
* deployments.
|
|
22
|
-
*/
|
|
8
|
+
/** Whether non-loopback http walnut/issuer URLs are permitted. */
|
|
23
9
|
allowInsecureIssuer: boolean;
|
|
24
10
|
}
|
|
25
11
|
/**
|
|
26
|
-
* `parseArgs`-shaped options
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* Node's `parseArgs` doesn't support `--no-` boolean negation
|
|
32
|
-
* natively, so the opt-out is registered as its own flag. Passing
|
|
33
|
-
* both `--allow-insecure-issuer` and `--no-allow-insecure-issuer` is
|
|
34
|
-
* treated as user error and rejected.
|
|
12
|
+
* `parseArgs`-shaped options shared by every CLI verb. `parseArgs`
|
|
13
|
+
* doesn't support `--no-` boolean negation natively, so the opt-out
|
|
14
|
+
* is registered as its own flag and `parseCommonArgs` rejects passing
|
|
15
|
+
* both together.
|
|
35
16
|
*/
|
|
36
17
|
export declare const COMMON_OPTIONS: NonNullable<ParseArgsConfig["options"]>;
|
|
37
18
|
/** Subset of `parseArgs(...).values` this helper consumes. */
|
|
@@ -40,43 +21,15 @@ export interface ParsedCommonValues {
|
|
|
40
21
|
"allow-insecure-issuer"?: boolean;
|
|
41
22
|
"no-allow-insecure-issuer"?: boolean;
|
|
42
23
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Subset of a stored entry used as a fallback for
|
|
45
|
-
* `allowInsecureIssuer` when the user passes neither
|
|
46
|
-
* `--allow-insecure-issuer` nor `--no-allow-insecure-issuer`.
|
|
47
|
-
* Sourced from `KeyringTokenStore.load()` by the dispatcher.
|
|
48
|
-
*
|
|
49
|
-
* `walnutURL` is carried alongside so the fallback only applies when
|
|
50
|
-
* the user is targeting the same deployment as the stored entry —
|
|
51
|
-
* otherwise a dev-time HTTP-allow setting would silently carry over
|
|
52
|
-
* to a prod login.
|
|
53
|
-
*/
|
|
24
|
+
/** Stored fallback for `allowInsecureIssuer` + the `walnutURL` it was minted against. */
|
|
54
25
|
export interface StoredCommonDefaults {
|
|
55
26
|
walnutURL: string;
|
|
56
27
|
allowInsecureIssuer: boolean;
|
|
57
28
|
}
|
|
58
29
|
/**
|
|
59
30
|
* Resolves common configuration from parsed flag values, falling
|
|
60
|
-
* back to `AXE_SERVER_URL`
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* `allowInsecureIssuer` is consumed by `login` (it is forwarded to
|
|
64
|
-
* SSO discovery and the OAuth flow). The fallback to a stored value
|
|
65
|
-
* exists so an interactive re-login on a private dev instance does
|
|
66
|
-
* not need the flag re-passed when the previous login set it. The
|
|
67
|
-
* fallback is gated on the resolved walnut URL matching the stored
|
|
68
|
-
* one: a user logging in to a different deployment must opt back in
|
|
69
|
-
* with `--allow-insecure-issuer` explicitly. The `token` and `logout`
|
|
70
|
-
* verbs do **not** consume this resolved value — they read
|
|
71
|
-
* `allowInsecureIssuer` directly from the keychain entry's metadata,
|
|
72
|
-
* so flag/env input is silently ignored there (and the help text for
|
|
73
|
-
* those verbs documents that).
|
|
74
|
-
*
|
|
75
|
-
* @param values The `values` object returned from `parseArgs`.
|
|
76
|
-
* @param env Environment to consult for fallback. Defaults to
|
|
77
|
-
* `process.env`; injected for test determinism.
|
|
78
|
-
* @param defaults Stored fallback for `allowInsecureIssuer` plus the
|
|
79
|
-
* `walnutURL` it was minted against. Pass `null` (or omit) when
|
|
80
|
-
* nothing is stored.
|
|
31
|
+
* back to `AXE_SERVER_URL` and finally to `DEFAULT_WALNUT_URL`.
|
|
32
|
+
* `allowInsecureIssuer` falls back to `defaults` only when the
|
|
33
|
+
* resolved walnut URL matches `defaults.walnutURL`.
|
|
81
34
|
*/
|
|
82
35
|
export declare function parseCommonArgs(values: ParsedCommonValues, env?: NodeJS.ProcessEnv, defaults?: StoredCommonDefaults | null): CommonArgs;
|
package/dist/cli/commonArgs.js
CHANGED
|
@@ -6,22 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.COMMON_OPTIONS = exports.DEFAULT_WALNUT_URL = void 0;
|
|
7
7
|
exports.parseCommonArgs = parseCommonArgs;
|
|
8
8
|
const remove_trailing_slash_1 = __importDefault(require("remove-trailing-slash"));
|
|
9
|
-
/**
|
|
10
|
-
* Default axe server URL for `axe-auth` users on Deque's SaaS prod
|
|
11
|
-
* deployment. The CLI's `--server` flag (and `AXE_SERVER_URL` env)
|
|
12
|
-
* override this; non-prod customers must supply their own walnut URL.
|
|
13
|
-
*/
|
|
9
|
+
/** Default axe server URL for Deque SaaS prod. */
|
|
14
10
|
exports.DEFAULT_WALNUT_URL = "https://axe.deque.com";
|
|
15
11
|
/**
|
|
16
|
-
* `parseArgs`-shaped options
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* Node's `parseArgs` doesn't support `--no-` boolean negation
|
|
22
|
-
* natively, so the opt-out is registered as its own flag. Passing
|
|
23
|
-
* both `--allow-insecure-issuer` and `--no-allow-insecure-issuer` is
|
|
24
|
-
* treated as user error and rejected.
|
|
12
|
+
* `parseArgs`-shaped options shared by every CLI verb. `parseArgs`
|
|
13
|
+
* doesn't support `--no-` boolean negation natively, so the opt-out
|
|
14
|
+
* is registered as its own flag and `parseCommonArgs` rejects passing
|
|
15
|
+
* both together.
|
|
25
16
|
*/
|
|
26
17
|
exports.COMMON_OPTIONS = {
|
|
27
18
|
server: { type: "string" },
|
|
@@ -30,27 +21,9 @@ exports.COMMON_OPTIONS = {
|
|
|
30
21
|
};
|
|
31
22
|
/**
|
|
32
23
|
* Resolves common configuration from parsed flag values, falling
|
|
33
|
-
* back to `AXE_SERVER_URL`
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* `allowInsecureIssuer` is consumed by `login` (it is forwarded to
|
|
37
|
-
* SSO discovery and the OAuth flow). The fallback to a stored value
|
|
38
|
-
* exists so an interactive re-login on a private dev instance does
|
|
39
|
-
* not need the flag re-passed when the previous login set it. The
|
|
40
|
-
* fallback is gated on the resolved walnut URL matching the stored
|
|
41
|
-
* one: a user logging in to a different deployment must opt back in
|
|
42
|
-
* with `--allow-insecure-issuer` explicitly. The `token` and `logout`
|
|
43
|
-
* verbs do **not** consume this resolved value — they read
|
|
44
|
-
* `allowInsecureIssuer` directly from the keychain entry's metadata,
|
|
45
|
-
* so flag/env input is silently ignored there (and the help text for
|
|
46
|
-
* those verbs documents that).
|
|
47
|
-
*
|
|
48
|
-
* @param values The `values` object returned from `parseArgs`.
|
|
49
|
-
* @param env Environment to consult for fallback. Defaults to
|
|
50
|
-
* `process.env`; injected for test determinism.
|
|
51
|
-
* @param defaults Stored fallback for `allowInsecureIssuer` plus the
|
|
52
|
-
* `walnutURL` it was minted against. Pass `null` (or omit) when
|
|
53
|
-
* nothing is stored.
|
|
24
|
+
* back to `AXE_SERVER_URL` and finally to `DEFAULT_WALNUT_URL`.
|
|
25
|
+
* `allowInsecureIssuer` falls back to `defaults` only when the
|
|
26
|
+
* resolved walnut URL matches `defaults.walnutURL`.
|
|
54
27
|
*/
|
|
55
28
|
function parseCommonArgs(values, env = process.env, defaults = null) {
|
|
56
29
|
const walnutURL = (0, remove_trailing_slash_1.default)(values.server ?? env.AXE_SERVER_URL ?? exports.DEFAULT_WALNUT_URL);
|
package/dist/cli/confirm.js
CHANGED
|
@@ -9,9 +9,6 @@ const node_readline_1 = require("node:readline");
|
|
|
9
9
|
* default action stays conservative.
|
|
10
10
|
*/
|
|
11
11
|
async function confirm(options) {
|
|
12
|
-
// Annotate after the ?? fallbacks so the union of `Writable |
|
|
13
|
-
// NodeJS.WriteStream` (from `process.stderr`) collapses to the
|
|
14
|
-
// base class — otherwise `output.write(...)` is ambiguous.
|
|
15
12
|
const input = options.input ?? process.stdin;
|
|
16
13
|
const output = options.output ?? process.stderr;
|
|
17
14
|
const question = options.question.endsWith(" ")
|
package/dist/cli/errors.d.ts
CHANGED
|
@@ -2,19 +2,12 @@
|
|
|
2
2
|
export type CLIErrorCode = "NOT_AUTHENTICATED" | "USER_CANCELLED" | "ALREADY_AUTHENTICATED" | "OAUTH_FAILED" | "KEYCHAIN_FAILURE";
|
|
3
3
|
/**
|
|
4
4
|
* Thrown from a verb's `run` to signal a known failure. The
|
|
5
|
-
* dispatcher
|
|
6
|
-
* with `exitCode`. The `code` field is the load-bearing
|
|
7
|
-
* discriminator; `exitCode` is derived for shell scripts and is
|
|
8
|
-
* documented in the README.
|
|
5
|
+
* dispatcher writes `message` to stderr and exits with `exitCode`.
|
|
9
6
|
*/
|
|
10
7
|
export declare class CLIError extends Error {
|
|
11
8
|
readonly code: CLIErrorCode;
|
|
12
9
|
readonly exitCode: number;
|
|
13
10
|
constructor(code: CLIErrorCode, message: string);
|
|
14
11
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Returns the `message` of an `Error`-shaped value, or its `String`
|
|
17
|
-
* coercion otherwise. Used in user-facing error templates so
|
|
18
|
-
* callers don't inline the `instanceof` ternary every time.
|
|
19
|
-
*/
|
|
12
|
+
/** Returns the `message` of an `Error`, or its `String` coercion otherwise. */
|
|
20
13
|
export declare function describeError(err: unknown): string;
|
package/dist/cli/errors.js
CHANGED
|
@@ -11,10 +11,7 @@ const EXIT_CODE_BY_ERROR_CODE = {
|
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
* Thrown from a verb's `run` to signal a known failure. The
|
|
14
|
-
* dispatcher
|
|
15
|
-
* with `exitCode`. The `code` field is the load-bearing
|
|
16
|
-
* discriminator; `exitCode` is derived for shell scripts and is
|
|
17
|
-
* documented in the README.
|
|
14
|
+
* dispatcher writes `message` to stderr and exits with `exitCode`.
|
|
18
15
|
*/
|
|
19
16
|
class CLIError extends Error {
|
|
20
17
|
code;
|
|
@@ -27,11 +24,7 @@ class CLIError extends Error {
|
|
|
27
24
|
}
|
|
28
25
|
}
|
|
29
26
|
exports.CLIError = CLIError;
|
|
30
|
-
/**
|
|
31
|
-
* Returns the `message` of an `Error`-shaped value, or its `String`
|
|
32
|
-
* coercion otherwise. Used in user-facing error templates so
|
|
33
|
-
* callers don't inline the `instanceof` ternary every time.
|
|
34
|
-
*/
|
|
27
|
+
/** Returns the `message` of an `Error`, or its `String` coercion otherwise. */
|
|
35
28
|
function describeError(err) {
|
|
36
29
|
return err instanceof Error ? err.message : String(err);
|
|
37
30
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Force-exit the process to avoid hanging on open connections.
|
|
3
|
+
*
|
|
4
|
+
* On Windows, briefly yield first so undici can finish closing pending sockets;
|
|
5
|
+
* otherwise the process aborts with a libuv UV_HANDLE_CLOSING assertion in
|
|
6
|
+
* `src\win\async.c`. See https://github.com/nodejs/node/issues/56645.
|
|
7
|
+
*/
|
|
8
|
+
export declare function safeExit(code: number): Promise<never>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.safeExit = safeExit;
|
|
4
|
+
/**
|
|
5
|
+
* Force-exit the process to avoid hanging on open connections.
|
|
6
|
+
*
|
|
7
|
+
* On Windows, briefly yield first so undici can finish closing pending sockets;
|
|
8
|
+
* otherwise the process aborts with a libuv UV_HANDLE_CLOSING assertion in
|
|
9
|
+
* `src\win\async.c`. See https://github.com/nodejs/node/issues/56645.
|
|
10
|
+
*/
|
|
11
|
+
async function safeExit(code) {
|
|
12
|
+
if (process.platform === "win32") {
|
|
13
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
14
|
+
}
|
|
15
|
+
process.exit(code);
|
|
16
|
+
}
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -4,48 +4,22 @@ import type { LoadResult } from "../oauth/tokenStore";
|
|
|
4
4
|
import type { CommonArgs } from "./commonArgs";
|
|
5
5
|
/** Mapping passed to `parseArgs` for verb-specific flags. */
|
|
6
6
|
export type VerbOptions = NonNullable<ParseArgsConfig["options"]>;
|
|
7
|
-
/**
|
|
8
|
-
* The injectables every CLI verb sees. The dispatcher fills these in
|
|
9
|
-
* with the real `process.*` streams; tests pass synthetic values.
|
|
10
|
-
* Verb-specific overrides (e.g. `getToken`, `authorize`) extend this
|
|
11
|
-
* interface in the relevant verb's module — TS allows the dispatcher
|
|
12
|
-
* to pass a plain `CommandDeps` because the verb-specific extras are
|
|
13
|
-
* declared optional.
|
|
14
|
-
*/
|
|
7
|
+
/** Injectables every CLI verb sees. Dispatcher fills with `process.*`; tests pass synthetic values. */
|
|
15
8
|
export interface CommandDeps {
|
|
16
|
-
/**
|
|
17
|
-
* Standard input. Carries an optional `isTTY` so verbs like
|
|
18
|
-
* `login` can branch on interactivity. Real `process.stdin`
|
|
19
|
-
* satisfies the shape; test fixtures using `Readable.from([])`
|
|
20
|
-
* leave `isTTY` undefined, which coerces to `false`.
|
|
21
|
-
*/
|
|
9
|
+
/** `isTTY` is optional so `Readable.from([])` test fixtures coerce to non-TTY. */
|
|
22
10
|
stdin: Readable & {
|
|
23
11
|
isTTY?: boolean;
|
|
24
12
|
};
|
|
25
13
|
stdout: Writable;
|
|
26
14
|
stderr: Writable;
|
|
27
15
|
/**
|
|
28
|
-
* Pre-loaded result of `KeyringTokenStore.load()
|
|
29
|
-
* dispatcher
|
|
30
|
-
*
|
|
31
|
-
* again so a single `axe-auth token` invocation hits the keychain
|
|
32
|
-
* once instead of three times. Tests typically leave this
|
|
33
|
-
* undefined and inject a fake `tokenStore` instead.
|
|
16
|
+
* Pre-loaded result of `KeyringTokenStore.load()` from the
|
|
17
|
+
* dispatcher's keychain read, so a single CLI invocation hits the
|
|
18
|
+
* keychain once instead of N times.
|
|
34
19
|
*/
|
|
35
20
|
loadedEntry?: LoadResult;
|
|
36
21
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Specification of a single CLI verb. The dispatcher in
|
|
39
|
-
* `src/index.ts` consumes one of these per registered command:
|
|
40
|
-
* parses argv, resolves common args, prints `helpText` for `--help`,
|
|
41
|
-
* runs `run`, and translates thrown `CLIError`s (see `./errors`)
|
|
42
|
-
* into exit codes.
|
|
43
|
-
*
|
|
44
|
-
* Each verb narrows the `run` parameters to its own
|
|
45
|
-
* `CommonArgs & <Verb>Flags` and `<Verb>Deps`. Method-shorthand
|
|
46
|
-
* bivariance lets that narrower signature satisfy this interface
|
|
47
|
-
* without casts at the verb definition.
|
|
48
|
-
*/
|
|
22
|
+
/** Specification of a single CLI verb consumed by the dispatcher. */
|
|
49
23
|
export interface CommandSpec {
|
|
50
24
|
/** Verb name, e.g. `"login"`. */
|
|
51
25
|
readonly name: string;
|
|
@@ -53,27 +27,13 @@ export interface CommandSpec {
|
|
|
53
27
|
readonly summary: string;
|
|
54
28
|
/** Full help text printed for `axe-auth <verb> --help`. */
|
|
55
29
|
readonly helpText: string;
|
|
56
|
-
/**
|
|
57
|
-
* Verb-specific `parseArgs` options. Common options
|
|
58
|
-
* (`--server` / `--allow-insecure-issuer` / `--no-allow-insecure-issuer`)
|
|
59
|
-
* are added by the dispatcher; do not duplicate them here.
|
|
60
|
-
*/
|
|
30
|
+
/** Verb-specific `parseArgs` options; common options are added by the dispatcher. */
|
|
61
31
|
readonly options: VerbOptions;
|
|
62
|
-
/**
|
|
63
|
-
* `true` if this verb cannot run without a usable walnut URL
|
|
64
|
-
* (login). `false` for verbs that operate on the stored entry
|
|
65
|
-
* alone (token, logout) and have their own "not authenticated" /
|
|
66
|
-
* "already logged out" handling for the empty-entry case. With
|
|
67
|
-
* the SaaS prod default in `parseCommonArgs`, the walnut URL is
|
|
68
|
-
* never strictly missing, but this flag remains for future
|
|
69
|
-
* required-config additions.
|
|
70
|
-
*/
|
|
32
|
+
/** `true` if this verb cannot run without a usable walnut URL (login). */
|
|
71
33
|
readonly requiresConfig: boolean;
|
|
72
34
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* explicit exit code; throw any other error for a generic exit
|
|
76
|
-
* code 2 plus the error message on stderr.
|
|
35
|
+
* Throw `CLIError` for a known failure with an explicit exit code;
|
|
36
|
+
* any other error becomes exit code 2 with the message on stderr.
|
|
77
37
|
*/
|
|
78
38
|
run(args: CommonArgs, deps: CommandDeps): Promise<void>;
|
|
79
39
|
}
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -17,10 +17,7 @@ export interface LoginDeps extends CommandDeps {
|
|
|
17
17
|
* so the pre-check and post-flow save agree on the keychain entry.
|
|
18
18
|
*/
|
|
19
19
|
tokenStore?: TokenStore;
|
|
20
|
-
/**
|
|
21
|
-
* Override the confirmation prompt (for tests). Receives the
|
|
22
|
-
* issuer URL and returns whether the user wants to proceed.
|
|
23
|
-
*/
|
|
20
|
+
/** Override the confirmation prompt (for tests). */
|
|
24
21
|
confirm?: (prompt: string) => Promise<boolean>;
|
|
25
22
|
}
|
|
26
23
|
/** Verb-specific flags for `axe-auth login`. */
|
package/dist/commands/login.js
CHANGED
|
@@ -32,9 +32,6 @@ const loginCommand = {
|
|
|
32
32
|
output: deps.stderr,
|
|
33
33
|
}));
|
|
34
34
|
const tokenStore = deps.tokenStore ?? new tokenStore_1.KeyringTokenStore();
|
|
35
|
-
// 1. Ask the axe server where its Keycloak lives and which client to use.
|
|
36
|
-
// This is the only step that hits the axe server directly; from here on
|
|
37
|
-
// the CLI talks to Keycloak using the discovered coordinates.
|
|
38
35
|
let ssoConfig;
|
|
39
36
|
try {
|
|
40
37
|
ssoConfig = await discoverFn(args.walnutURL, {
|
|
@@ -49,16 +46,11 @@ const loginCommand = {
|
|
|
49
46
|
}
|
|
50
47
|
const issuerURL = `${(0, remove_trailing_slash_1.default)(ssoConfig.url)}/auth/realms/${ssoConfig.realm}`;
|
|
51
48
|
const clientId = ssoConfig.mcpClientId;
|
|
52
|
-
// 2. Re-auth confirmation. Same UX as the previous flag-driven
|
|
53
|
-
// flow, but the comparison is against the *discovered* issuer
|
|
54
|
-
// + client, not user-supplied flags.
|
|
55
49
|
if (!args.force) {
|
|
56
50
|
const stored = deps.loadedEntry ?? (await tokenStore.load());
|
|
57
51
|
if (!stored.ok && stored.reason !== "empty") {
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
// either way, but the user deserves a breadcrumb for what
|
|
61
|
-
// disappeared.
|
|
52
|
+
// authorize() will overwrite either way, but the user
|
|
53
|
+
// deserves a breadcrumb for what disappeared.
|
|
62
54
|
deps.stderr.write(`axe-auth: replacing unreadable stored credentials (${stored.reason}).\n`);
|
|
63
55
|
}
|
|
64
56
|
if (stored.ok) {
|
|
@@ -86,9 +78,8 @@ const loginCommand = {
|
|
|
86
78
|
}
|
|
87
79
|
}
|
|
88
80
|
}
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
// revoke) can operate without user-supplied flags.
|
|
81
|
+
// `walnutURL` lands in the StoredEntry so future verbs
|
|
82
|
+
// (re-discovery, revoke) operate without user-supplied flags.
|
|
92
83
|
try {
|
|
93
84
|
await authorizeFn({
|
|
94
85
|
issuerURL,
|
|
@@ -97,7 +88,7 @@ const loginCommand = {
|
|
|
97
88
|
scopes: ["offline_access"],
|
|
98
89
|
allowInsecureIssuer: args.allowInsecureIssuer,
|
|
99
90
|
tokenStore,
|
|
100
|
-
|
|
91
|
+
onAuthorizationURL: (url) => {
|
|
101
92
|
deps.stderr.write(`Authorization URL: ${url}\n`);
|
|
102
93
|
},
|
|
103
94
|
onWarning: (msg) => {
|
package/dist/commands/logout.js
CHANGED
|
@@ -16,8 +16,6 @@ const logoutCommand = {
|
|
|
16
16
|
const discoverFn = deps.discoverOIDC ?? discoverOIDC_1.discoverOIDC;
|
|
17
17
|
const revokeFn = deps.revokeRefreshToken ?? revokeToken_1.revokeRefreshToken;
|
|
18
18
|
const tokenStore = deps.tokenStore ?? new tokenStore_1.KeyringTokenStore();
|
|
19
|
-
// Prefer the entry the dispatcher already loaded; only fall back
|
|
20
|
-
// to a fresh read when there isn't one (test path).
|
|
21
19
|
const loaded = deps.loadedEntry ?? (await tokenStore.load());
|
|
22
20
|
if (!loaded.ok) {
|
|
23
21
|
if (loaded.reason === "empty") {
|
package/dist/commands/token.js
CHANGED
|
@@ -15,10 +15,6 @@ const tokenCommand = {
|
|
|
15
15
|
async run(_args, deps) {
|
|
16
16
|
const getToken = deps.getToken ?? getValidAccessToken_1.getValidAccessToken;
|
|
17
17
|
const tokenStore = deps.tokenStore ?? new tokenStore_1.KeyringTokenStore();
|
|
18
|
-
// Stored entry is the only source of truth: single-entry-per-machine
|
|
19
|
-
// model, the blob carries the issuer/client coordinates the tokens
|
|
20
|
-
// were minted against, and there is no longer a flag to override
|
|
21
|
-
// them with.
|
|
22
18
|
const loaded = deps.loadedEntry ?? (await tokenStore.load());
|
|
23
19
|
let token;
|
|
24
20
|
try {
|
package/dist/index.js
CHANGED
|
@@ -13,8 +13,8 @@ const login_1 = __importDefault(require("./commands/login"));
|
|
|
13
13
|
const logout_1 = __importDefault(require("./commands/logout"));
|
|
14
14
|
const token_1 = __importDefault(require("./commands/token"));
|
|
15
15
|
const errors_1 = require("./cli/errors");
|
|
16
|
+
const safeExit_1 = require("./cli/safeExit");
|
|
16
17
|
const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "package.json"), "utf-8"));
|
|
17
|
-
// Iteration order is the order verbs appear in `axe-auth --help`.
|
|
18
18
|
const COMMANDS = [
|
|
19
19
|
login_1.default,
|
|
20
20
|
logout_1.default,
|
|
@@ -82,14 +82,9 @@ async function dispatch(argv) {
|
|
|
82
82
|
process.stdout.write(`${command.helpText}\n`);
|
|
83
83
|
return 0;
|
|
84
84
|
}
|
|
85
|
-
// Best-effort load
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
// `axe-auth token` invocation hits the keychain once instead of
|
|
89
|
-
// twice. A read failure here is non-fatal — `parseCommonArgs`
|
|
90
|
-
// always succeeds (the SaaS prod default fills any unsupplied
|
|
91
|
-
// walnut URL), and verbs that need a stored entry have their own
|
|
92
|
-
// empty/corrupt-entry handling.
|
|
85
|
+
// Best-effort load: handed to verbs via `deps.loadedEntry` so a
|
|
86
|
+
// single CLI invocation hits the keychain once. Read failure is
|
|
87
|
+
// non-fatal — verbs handle their own empty/corrupt cases.
|
|
93
88
|
let defaults = null;
|
|
94
89
|
let loadedEntry;
|
|
95
90
|
try {
|
|
@@ -102,9 +97,8 @@ async function dispatch(argv) {
|
|
|
102
97
|
}
|
|
103
98
|
}
|
|
104
99
|
catch {
|
|
105
|
-
// Keychain unavailable: leave defaults null.
|
|
106
|
-
//
|
|
107
|
-
// here; token / logout's own empty-entry path handles it.
|
|
100
|
+
// Keychain unavailable: leave defaults null. Verbs surface their
|
|
101
|
+
// own clearer errors at the actual save / read site.
|
|
108
102
|
}
|
|
109
103
|
let common;
|
|
110
104
|
try {
|
|
@@ -133,7 +127,7 @@ async function dispatch(argv) {
|
|
|
133
127
|
return 2;
|
|
134
128
|
}
|
|
135
129
|
}
|
|
136
|
-
dispatch(process.argv.slice(2)).then((code) =>
|
|
130
|
+
dispatch(process.argv.slice(2)).then((code) => (0, safeExit_1.safeExit)(code), (err) => {
|
|
137
131
|
process.stderr.write(`${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
|
|
138
|
-
|
|
132
|
+
return (0, safeExit_1.safeExit)(1);
|
|
139
133
|
});
|
|
@@ -5,17 +5,12 @@ export interface BuildAuthorizationURLOptions {
|
|
|
5
5
|
/** OAuth client identifier registered with the authorization server. */
|
|
6
6
|
clientId: string;
|
|
7
7
|
/** Loopback redirect URI the callback server is listening on. */
|
|
8
|
-
|
|
8
|
+
redirectURI: string;
|
|
9
9
|
/** PKCE `code_challenge` derived via S256. */
|
|
10
10
|
codeChallenge: string;
|
|
11
11
|
/** CSRF `state` value, echoed by the auth server and validated on callback. */
|
|
12
12
|
state: string;
|
|
13
|
-
/**
|
|
14
|
-
* OAuth scopes to request. No default — callers must choose explicitly.
|
|
15
|
-
* Keycloak-idiomatic value for a refresh-token flow is `["offline_access"]`;
|
|
16
|
-
* Google uses `access_type=offline` (a custom parameter) instead; Auth0
|
|
17
|
-
* tolerates `offline_access` only with specific audience settings.
|
|
18
|
-
*/
|
|
13
|
+
/** OAuth scopes to request. No default; callers choose explicitly. */
|
|
19
14
|
scopes: readonly string[];
|
|
20
15
|
}
|
|
21
16
|
/**
|
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildAuthorizationURL = buildAuthorizationURL;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// something is wrong on the server side (or with the caller's
|
|
8
|
-
// endpoint override) and silently keeping both values would be a
|
|
9
|
-
// security trap: the authorization server's disambiguation is
|
|
10
|
-
// unspecified and varies by implementation.
|
|
5
|
+
// Pre-existing values for these on the authorization endpoint are a
|
|
6
|
+
// security trap: the auth server's disambiguation is unspecified.
|
|
11
7
|
const OAUTH_REQUIRED_PARAMS = [
|
|
12
8
|
"response_type",
|
|
13
9
|
"client_id",
|
|
@@ -43,7 +39,7 @@ function buildAuthorizationURL(options) {
|
|
|
43
39
|
// `set` just reads more conventionally.
|
|
44
40
|
url.searchParams.set("response_type", "code");
|
|
45
41
|
url.searchParams.set("client_id", options.clientId);
|
|
46
|
-
url.searchParams.set("redirect_uri", options.
|
|
42
|
+
url.searchParams.set("redirect_uri", options.redirectURI);
|
|
47
43
|
url.searchParams.set("code_challenge", options.codeChallenge);
|
|
48
44
|
url.searchParams.set("code_challenge_method", "S256");
|
|
49
45
|
url.searchParams.set("state", options.state);
|
|
@@ -2,72 +2,34 @@ import type { TokenSet } from "./tokenResponse";
|
|
|
2
2
|
import { type TokenStore } from "./tokenStore";
|
|
3
3
|
/** Options for `authorize`. */
|
|
4
4
|
export interface AuthorizeOptions {
|
|
5
|
-
/**
|
|
6
|
-
* Authorization-server URL the discovery document claims as its
|
|
7
|
-
* `issuer`. For Keycloak, callers build this as
|
|
8
|
-
* `${serverURL}/realms/${realm}`. For other providers it is the
|
|
9
|
-
* hostname (or issuer path) advertised in their discovery document.
|
|
10
|
-
*/
|
|
5
|
+
/** Issuer URL the OIDC discovery document advertises (e.g. `${serverURL}/realms/${realm}` for Keycloak). */
|
|
11
6
|
issuerURL: string;
|
|
12
7
|
/** OAuth client ID registered with the authorization server. */
|
|
13
8
|
clientId: string;
|
|
14
|
-
/**
|
|
15
|
-
* Originating walnut (axe server) URL the user supplied (or the
|
|
16
|
-
* SaaS prod default) at login. Persisted in the stored entry
|
|
17
|
-
* alongside the OAuth coordinates so future verbs can re-discover
|
|
18
|
-
* `/api/sso-config` without user-supplied flags.
|
|
19
|
-
*/
|
|
9
|
+
/** Persisted alongside the tokens so future verbs can re-discover `/api/sso-config` without flags. */
|
|
20
10
|
walnutURL: string;
|
|
21
|
-
/**
|
|
22
|
-
* OAuth scopes to request. Required — this library has no opinion
|
|
23
|
-
* about which scopes your provider expects. Keycloak callers who
|
|
24
|
-
* want a refresh token typically pass `["offline_access"]`; Google
|
|
25
|
-
* uses `access_type=offline` as a separate query param and
|
|
26
|
-
* therefore needs an empty scope list plus that param threaded
|
|
27
|
-
* through elsewhere.
|
|
28
|
-
*/
|
|
11
|
+
/** OAuth scopes to request. Keycloak callers typically pass `["offline_access"]` for a refresh token. */
|
|
29
12
|
scopes: readonly string[];
|
|
30
13
|
/** Max time to wait for the loopback callback, in milliseconds. */
|
|
31
14
|
timeoutMs?: number;
|
|
32
15
|
/** Aborts the in-flight discovery, callback wait, and token exchange. */
|
|
33
16
|
signal?: AbortSignal;
|
|
34
|
-
/**
|
|
35
|
-
* Override for the token persistence layer. Defaults to a fresh
|
|
36
|
-
* `KeyringTokenStore()` (single keychain entry per machine; the
|
|
37
|
-
* blob carries its own issuer/client coordinates).
|
|
38
|
-
*/
|
|
17
|
+
/** Override for the token persistence layer. */
|
|
39
18
|
tokenStore?: TokenStore;
|
|
40
19
|
/** Override for the system browser launcher. Injected for tests. */
|
|
41
20
|
openBrowser?: (url: string) => void;
|
|
21
|
+
/** Called with the authorization URL just before the browser launch. Default prints to stderr only when stderr is a TTY. */
|
|
22
|
+
onAuthorizationURL?: (url: string) => void;
|
|
42
23
|
/**
|
|
43
|
-
* Called
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
onAuthorizationUrl?: (url: string) => void;
|
|
50
|
-
/**
|
|
51
|
-
* Called for soft warnings that are not errors but warrant user
|
|
52
|
-
* attention (e.g. `offline_access` was requested but the server did
|
|
53
|
-
* not return a `refresh_token`, or the browser failed to launch).
|
|
54
|
-
* The default prints to stderr only when stderr is a TTY. Pass a
|
|
55
|
-
* custom handler to route warnings through your own UI, or `() =>
|
|
56
|
-
* {}` to suppress entirely.
|
|
57
|
-
*
|
|
58
|
-
* Non-TTY callers who want warning visibility (log files, parent
|
|
59
|
-
* CLIs, background workers) should pass an explicit handler.
|
|
60
|
-
* Dropped warnings have no visible symptom at the time they fire —
|
|
61
|
-
* users only discover the consequence later (e.g. being prompted to
|
|
62
|
-
* re-authenticate at the next session).
|
|
24
|
+
* Called for soft warnings (e.g. requested `offline_access` but the
|
|
25
|
+
* server returned no refresh token, or the browser failed to
|
|
26
|
+
* launch). Default prints to stderr only when stderr is a TTY.
|
|
27
|
+
* Non-TTY callers who want warning visibility should pass an
|
|
28
|
+
* explicit handler — dropped warnings have no symptom at the time
|
|
29
|
+
* they fire; users discover the consequence later.
|
|
63
30
|
*/
|
|
64
31
|
onWarning?: (message: string) => void;
|
|
65
|
-
/**
|
|
66
|
-
* Forwarded to the discovery step. Loopback hosts (`localhost` /
|
|
67
|
-
* `127.0.0.1` / `[::1]`) are always permitted over http; this flag
|
|
68
|
-
* is the opt-in for non-loopback http issuers and for non-loopback
|
|
69
|
-
* http endpoints returned by discovery. Default `false`.
|
|
70
|
-
*/
|
|
32
|
+
/** Forwarded to discovery; permits non-loopback http issuers + endpoints. */
|
|
71
33
|
allowInsecureIssuer?: boolean;
|
|
72
34
|
}
|
|
73
35
|
/**
|