@deque/axe-auth 1.1.0-next.cc7aaec1 → 1.1.0-next.cd6c9e1a

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 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",
@@ -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 describing the flags every CLI verb
27
- * accepts (--server, --allow-insecure-issuer, --no-allow-insecure-issuer).
28
- * Subcommands spread this into their own `options` so they can add
29
- * verb-specific flags alongside.
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` when `--server` is absent and finally to
61
- * the SaaS prod walnut URL when neither is set.
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;
@@ -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 describing the flags every CLI verb
17
- * accepts (--server, --allow-insecure-issuer, --no-allow-insecure-issuer).
18
- * Subcommands spread this into their own `options` so they can add
19
- * verb-specific flags alongside.
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` when `--server` is absent and finally to
34
- * the SaaS prod walnut URL when neither is set.
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);
@@ -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(" ")
@@ -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 in `src/index.ts` writes `message` to stderr and exits
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;
@@ -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 in `src/index.ts` writes `message` to stderr and exits
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
  }
@@ -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()`, populated by the
29
- * dispatcher when it ran the keychain read to derive the
30
- * `parseCommonArgs` fallback. Verbs prefer this over loading
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
- * Run the verb with already-resolved args and the dispatcher's
74
- * deps. Throw a `CLIError` to signal a known failure with an
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
  }
@@ -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`. */
@@ -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
- // Existing entry is unreadable (corrupt or stored under a
59
- // schema we can't migrate). authorize() will overwrite it
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
- // 3. Drive the OAuth flow. The originating axe server URL rides along
90
- // so it lands in the StoredEntry and future verbs (re-discovery,
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,
@@ -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") {
@@ -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
@@ -14,7 +14,6 @@ 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
16
  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
17
  const COMMANDS = [
19
18
  login_1.default,
20
19
  logout_1.default,
@@ -82,14 +81,9 @@ async function dispatch(argv) {
82
81
  process.stdout.write(`${command.helpText}\n`);
83
82
  return 0;
84
83
  }
85
- // Best-effort load of the stored entry. Used to (a) supply the
86
- // `allowInsecureIssuer` fallback on flag-free invocations, and (b)
87
- // hand the entry to the verb via `deps.loadedEntry` so a single
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.
84
+ // Best-effort load: handed to verbs via `deps.loadedEntry` so a
85
+ // single CLI invocation hits the keychain once. Read failure is
86
+ // non-fatal verbs handle their own empty/corrupt cases.
93
87
  let defaults = null;
94
88
  let loadedEntry;
95
89
  try {
@@ -102,9 +96,8 @@ async function dispatch(argv) {
102
96
  }
103
97
  }
104
98
  catch {
105
- // Keychain unavailable: leave defaults null. login will fail at
106
- // `tokenStore.save()` with a clearer error than we can produce
107
- // here; token / logout's own empty-entry path handles it.
99
+ // Keychain unavailable: leave defaults null. Verbs surface their
100
+ // own clearer errors at the actual save / read site.
108
101
  }
109
102
  let common;
110
103
  try {
@@ -10,12 +10,7 @@ export interface BuildAuthorizationURLOptions {
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
- // Names of OAuth params we always set. If any of these are already
6
- // present on the authorization endpoint URL returned by discovery,
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",
@@ -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;
42
- /**
43
- * Called with the authorization URL just before the browser launch.
44
- * The default prints to stderr only when stderr is a TTY, so a
45
- * parent CLI consuming this library as a dependency does not
46
- * double-print. Pass a custom handler to route the URL through your
47
- * own UI, or `() => {}` to suppress entirely.
48
- */
21
+ /** Called with the authorization URL just before the browser launch. Default prints to stderr only when stderr is a TTY. */
49
22
  onAuthorizationUrl?: (url: string) => void;
50
23
  /**
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
  /**
@@ -39,10 +39,8 @@ function defaultOnWarning(message) {
39
39
  */
40
40
  async function authorize(options) {
41
41
  const { issuerURL, clientId, walnutURL, scopes, timeoutMs, signal, tokenStore = new tokenStore_1.KeyringTokenStore(), openBrowser = openBrowser_1.openBrowser, onAuthorizationUrl = defaultOnAuthorizationUrl, onWarning = defaultOnWarning, allowInsecureIssuer, } = options;
42
- // Discovery first. If the auth server is unreachable we want to fail
43
- // *before* opening a browser — a rejected discovery throw is
44
- // strictly more useful than a browser tab pointing at a
45
- // wrong/unreachable URL.
42
+ // Discovery before browser-launch so a bad URL surfaces as a
43
+ // throw rather than a wrong/unreachable browser tab.
46
44
  const config = await (0, discoverOIDC_1.discoverOIDC)(issuerURL, {
47
45
  signal,
48
46
  allowInsecureIssuer,
@@ -15,36 +15,19 @@ export interface OIDCConfiguration {
15
15
  export interface DiscoverOIDCOptions {
16
16
  /** Aborts the underlying fetch when fired. */
17
17
  signal?: AbortSignal;
18
- /**
19
- * Permit non-HTTPS issuer URLs whose host is not a loopback literal.
20
- * Loopback hosts (`localhost`, `127.0.0.1`, `[::1]`) are always
21
- * allowed over http since they cannot be intercepted remotely; this
22
- * flag is for corporate dev setups or reverse-proxy scenarios where
23
- * http is the only available path. Default `false`.
24
- */
18
+ /** Permit non-HTTPS issuer URLs whose host is not a loopback literal. Default `false`. */
25
19
  allowInsecureIssuer?: boolean;
26
20
  }
27
21
  /**
28
- * Fetches and parses the OpenID Connect discovery document for a given
29
- * issuer. Fails fast (no retry) so the caller does not open a browser
30
- * against an unreachable authorization server.
22
+ * Fetches and parses the OIDC discovery document. Fails fast (no
23
+ * retry) so the caller does not open a browser against an unreachable
24
+ * authorization server. Verifies the server's claimed `issuer` matches
25
+ * the input URL per OIDC Discovery §3 — without this, a hostile
26
+ * discovery response could redirect the authorization and token
27
+ * endpoints to attacker hosts.
31
28
  *
32
- * This function uses the OIDC discovery well-known path as a
33
- * convention most OAuth 2.0 providers expose it regardless of
34
- * whether you intend to perform identity validation. This library
35
- * itself does not perform OIDC identity validation (no id_token /
36
- * nonce / signature checks); callers needing OIDC-strength identity
37
- * assurance should layer that on top.
38
- *
39
- * Verifies that the server's claimed `issuer` matches the URL the
40
- * caller passed in, per OIDC Discovery §3 / defence against a hostile
41
- * discovery response redirecting `authorization_endpoint` and
42
- * `token_endpoint` to attacker-controlled hosts.
43
- *
44
- * @param issuerURL Authorization-server URL the discovery document
45
- * claims as its `issuer`. For Keycloak, callers build this as
46
- * `${serverURL}/realms/${realm}`. For other providers it is the
47
- * hostname (or issuer path) advertised in their discovery document.
48
- * Trailing slashes tolerated.
29
+ * Uses the OIDC well-known path as a convention; does not perform
30
+ * OIDC-strength identity validation (no id_token / nonce / signature
31
+ * checks). Callers needing identity assurance should layer that on top.
49
32
  */
50
33
  export declare function discoverOIDC(issuerURL: string, options?: DiscoverOIDCOptions): Promise<OIDCConfiguration>;