@deque/axe-auth 1.1.0-next.73fce274 → 1.1.0-next.74c778ee
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.
|
@@ -4,6 +4,7 @@ exports.discoverOIDC = discoverOIDC;
|
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
5
|
const issuerURL_1 = require("./issuerURL");
|
|
6
6
|
const predicates_1 = require("./predicates");
|
|
7
|
+
const userAgent_1 = require("../userAgent");
|
|
7
8
|
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
8
9
|
function optionalString(v) {
|
|
9
10
|
return (0, predicates_1.isNonEmptyString)(v) ? v : undefined;
|
|
@@ -69,6 +70,31 @@ function parseConfiguration(body, url) {
|
|
|
69
70
|
endSessionEndpoint: optionalString(body.end_session_endpoint),
|
|
70
71
|
};
|
|
71
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* `code_challenge_methods_supported` is OPTIONAL in OIDC discovery, so its
|
|
75
|
+
* absence proves nothing — older providers may support PKCE without
|
|
76
|
+
* advertising it. But when the list IS present and does not include
|
|
77
|
+
* `S256` (the only method this CLI uses, per RFC 7636), the server has
|
|
78
|
+
* explicitly declared it does not support the flow we need. Fail fast
|
|
79
|
+
* with an actionable message instead of letting the user hit a generic
|
|
80
|
+
* OAuth error several steps deeper into the flow.
|
|
81
|
+
*
|
|
82
|
+
* An empty list (`[]`) is treated the same as a populated list missing
|
|
83
|
+
* `S256`: the server has explicitly advertised zero supported methods,
|
|
84
|
+
* which is incompatible.
|
|
85
|
+
*
|
|
86
|
+
* Called from `discoverOIDC` after issuer verification so that a
|
|
87
|
+
* tampered discovery doc surfaces the more security-critical issuer
|
|
88
|
+
* mismatch first.
|
|
89
|
+
*/
|
|
90
|
+
function assertPKCESupport(body, url) {
|
|
91
|
+
const methods = body.code_challenge_methods_supported;
|
|
92
|
+
if (!Array.isArray(methods))
|
|
93
|
+
return;
|
|
94
|
+
if (methods.includes("S256"))
|
|
95
|
+
return;
|
|
96
|
+
throw new errors_1.OAuthFlowError("DISCOVERY_FAILED", `OpenID configuration at ${url} advertises code_challenge_methods_supported = ${JSON.stringify(methods)}, but axe-auth requires S256 (PKCE per RFC 7636). The OAuth client used by axe-auth needs PKCE enabled, or you may be on an axe server version that predates OAuth-based MCP authentication.`);
|
|
97
|
+
}
|
|
72
98
|
/**
|
|
73
99
|
* Fetches and parses the OpenID Connect discovery document for a given
|
|
74
100
|
* issuer. Fails fast (no retry) so the caller does not open a browser
|
|
@@ -98,7 +124,10 @@ async function discoverOIDC(issuerURL, options = {}) {
|
|
|
98
124
|
const url = buildDiscoveryURL(issuerURL);
|
|
99
125
|
let response;
|
|
100
126
|
try {
|
|
101
|
-
response = await fetch(url, {
|
|
127
|
+
response = await fetch(url, {
|
|
128
|
+
headers: { "User-Agent": userAgent_1.USER_AGENT },
|
|
129
|
+
signal: options.signal,
|
|
130
|
+
});
|
|
102
131
|
}
|
|
103
132
|
catch (cause) {
|
|
104
133
|
throw new errors_1.OAuthFlowError("DISCOVERY_FAILED", `Could not reach the authentication server at ${url}. Check the URL and your network connection.`, { cause });
|
|
@@ -139,5 +168,6 @@ async function discoverOIDC(issuerURL, options = {}) {
|
|
|
139
168
|
if (config.endSessionEndpoint) {
|
|
140
169
|
assertSecureURL(config.endSessionEndpoint, "end_session_endpoint", allowInsecure);
|
|
141
170
|
}
|
|
171
|
+
assertPKCESupport(body, url);
|
|
142
172
|
return config;
|
|
143
173
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.refreshTokens = refreshTokens;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
5
|
const tokenResponse_1 = require("./tokenResponse");
|
|
6
|
+
const userAgent_1 = require("../userAgent");
|
|
6
7
|
/**
|
|
7
8
|
* Exchanges a refresh token for a fresh access token via RFC 6749 §6.
|
|
8
9
|
*
|
|
@@ -39,6 +40,7 @@ async function refreshTokens(options) {
|
|
|
39
40
|
headers: {
|
|
40
41
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
41
42
|
Accept: "application/json",
|
|
43
|
+
"User-Agent": userAgent_1.USER_AGENT,
|
|
42
44
|
},
|
|
43
45
|
body,
|
|
44
46
|
signal: options.signal,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.revokeRefreshToken = revokeRefreshToken;
|
|
4
|
+
const userAgent_1 = require("../userAgent");
|
|
4
5
|
/**
|
|
5
6
|
* Revokes a refresh token via RFC 7009. Servers SHOULD return 200
|
|
6
7
|
* regardless of whether the token was valid (the spec doesn't want
|
|
@@ -27,7 +28,10 @@ async function revokeRefreshToken(options) {
|
|
|
27
28
|
try {
|
|
28
29
|
response = await fetch(options.revocationEndpoint, {
|
|
29
30
|
method: "POST",
|
|
30
|
-
headers: {
|
|
31
|
+
headers: {
|
|
32
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
33
|
+
"User-Agent": userAgent_1.USER_AGENT,
|
|
34
|
+
},
|
|
31
35
|
body,
|
|
32
36
|
signal: options.signal,
|
|
33
37
|
});
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.exchangeCodeForTokens = exchangeCodeForTokens;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
5
|
const tokenResponse_1 = require("./tokenResponse");
|
|
6
|
+
const userAgent_1 = require("../userAgent");
|
|
6
7
|
/**
|
|
7
8
|
* Exchanges an authorization code for a `TokenSet` via the
|
|
8
9
|
* authorization server's token endpoint (RFC 6749 §4.1.3 + RFC 7636
|
|
@@ -27,6 +28,7 @@ async function exchangeCodeForTokens(options) {
|
|
|
27
28
|
headers: {
|
|
28
29
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
29
30
|
Accept: "application/json",
|
|
31
|
+
"User-Agent": userAgent_1.USER_AGENT,
|
|
30
32
|
},
|
|
31
33
|
body,
|
|
32
34
|
signal: options.signal,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `User-Agent` header value sent on all outbound requests, per
|
|
3
|
+
* Service Development Standards §4.4.
|
|
4
|
+
*
|
|
5
|
+
* Format: `axe-auth/v<package-version>` (e.g. `axe-auth/v1.0.2`).
|
|
6
|
+
*
|
|
7
|
+
* The npm scope (`@deque/`) is deliberately omitted from the wire format:
|
|
8
|
+
* `@` and `/` are not valid `tchar` per RFC 9110 §5.6.2, so a token like
|
|
9
|
+
* `@deque/axe-auth` would make the User-Agent malformed and risk WAF
|
|
10
|
+
* rejection (e.g. OWASP CRS rule 920330).
|
|
11
|
+
*/
|
|
12
|
+
export declare const USER_AGENT: string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.USER_AGENT = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "package.json"), "utf-8"));
|
|
7
|
+
/**
|
|
8
|
+
* `User-Agent` header value sent on all outbound requests, per
|
|
9
|
+
* Service Development Standards §4.4.
|
|
10
|
+
*
|
|
11
|
+
* Format: `axe-auth/v<package-version>` (e.g. `axe-auth/v1.0.2`).
|
|
12
|
+
*
|
|
13
|
+
* The npm scope (`@deque/`) is deliberately omitted from the wire format:
|
|
14
|
+
* `@` and `/` are not valid `tchar` per RFC 9110 §5.6.2, so a token like
|
|
15
|
+
* `@deque/axe-auth` would make the User-Agent malformed and risk WAF
|
|
16
|
+
* rejection (e.g. OWASP CRS rule 920330).
|
|
17
|
+
*/
|
|
18
|
+
exports.USER_AGENT = `axe-auth/v${pkg.version}`;
|