soajs.controller 4.3.26 → 4.3.27
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/docs/mcp.md +3 -0
- package/mw/soajsRoutes/mcp/auth.js +11 -2
- package/package.json +1 -1
- package/test/unit/mw/soajsRoutes/mcpAuth.js +22 -0
package/docs/mcp.md
CHANGED
|
@@ -92,6 +92,9 @@ is yours, named by its endpoints.
|
|
|
92
92
|
- else `byHost`, by the host the request came in on (`X-Forwarded-Host`, else `Host`);
|
|
93
93
|
- else `default`.
|
|
94
94
|
- An entry needs `authorization_endpoint` and `token_endpoint`; `registration_endpoint` is optional.
|
|
95
|
+
- **`<origin>`** below is the scheme and host the request came in on: `X-Forwarded-Proto` and
|
|
96
|
+
`X-Forwarded-Host` when set. Behind a proxy that does not pass the scheme on, set
|
|
97
|
+
`"scheme": "https"` in `auth`; the host still comes from the request.
|
|
95
98
|
- **No token, or an unknown or expired one:** HTTP 401 with
|
|
96
99
|
`WWW-Authenticate: Bearer resource_metadata="<origin>/.well-known/oauth-protected-resource/soajs/mcp"`,
|
|
97
100
|
plus `?key=` when the client's URL had one. If nothing matches the request, the answer is as without
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* at the endpoints the configuration names.
|
|
17
17
|
*
|
|
18
18
|
* Which endpoints: by the product of the key on the request (auth.byProduct), else by the host the
|
|
19
|
-
* request came in on (auth.byHost), else auth.default.
|
|
19
|
+
* request came in on (auth.byHost), else auth.default. The addresses the gateway gives out use the
|
|
20
|
+
* request's host and auth.scheme, else the scheme the request came in with.
|
|
20
21
|
*
|
|
21
22
|
* An MCP client sends the token only, no key. A token restricted to one key (user.restrictedTo.key)
|
|
22
23
|
* brings it: /soajs/mcp without a key uses it, and with a key of the same product the token's key
|
|
@@ -61,8 +62,16 @@ function requestHost(req) {
|
|
|
61
62
|
return (firstValue(req.headers["x-forwarded-host"]) || req.headers.host || "").toLowerCase();
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* The scheme is auth.scheme when set, for a proxy that does not pass X-Forwarded-Proto on, else
|
|
67
|
+
* X-Forwarded-Proto, else the connection's.
|
|
68
|
+
*/
|
|
64
69
|
function requestOrigin(req) {
|
|
65
|
-
let
|
|
70
|
+
let auth = getAuthConfig(req);
|
|
71
|
+
let proto = (auth && (auth.scheme === "https" || auth.scheme === "http")) ? auth.scheme : null;
|
|
72
|
+
if (!proto) {
|
|
73
|
+
proto = firstValue(req.headers["x-forwarded-proto"]);
|
|
74
|
+
}
|
|
66
75
|
if (!proto) {
|
|
67
76
|
proto = (req.socket && req.socket.encrypted) ? "https" : "http";
|
|
68
77
|
}
|
package/package.json
CHANGED
|
@@ -276,5 +276,27 @@ describe("Unit test for: mw - soajsRoutes mcp auth", () => {
|
|
|
276
276
|
req.socket = {encrypted: false};
|
|
277
277
|
assert.strictEqual(auth.requestOrigin(req), "http://api.connect.example");
|
|
278
278
|
});
|
|
279
|
+
it("requestOrigin: auth.scheme wins over the forwarded proto, the host stays", () => {
|
|
280
|
+
let req = makeReq({headers: {"x-forwarded-proto": "http"}, mcp: {enabled: true, auth: Object.assign({scheme: "https"}, AUTH)}});
|
|
281
|
+
assert.strictEqual(auth.requestOrigin(req), "https://api.connect.example");
|
|
282
|
+
req = makeReq({headers: {"x-forwarded-proto": "http"}, mcp: {enabled: true, auth: Object.assign({scheme: "ftp"}, AUTH)}});
|
|
283
|
+
assert.strictEqual(auth.requestOrigin(req), "http://api.connect.example");
|
|
284
|
+
});
|
|
285
|
+
it("auth.scheme in the metadata and the 401", (done) => {
|
|
286
|
+
let mcp = {enabled: true, auth: Object.assign({scheme: "https"}, AUTH)};
|
|
287
|
+
let mw = auth.wellKnown({provision: provision});
|
|
288
|
+
run(mw, makeReq({method: "GET", url: "/.well-known/oauth-protected-resource", mcp: mcp, headers: {"x-forwarded-proto": "http"}}), (answer) => {
|
|
289
|
+
assert.strictEqual(answer.body.resource, "https://api.connect.example/soajs/mcp");
|
|
290
|
+
assert.deepStrictEqual(answer.body.authorization_servers, ["https://api.connect.example/soajs/mcp/auth/default"]);
|
|
291
|
+
run(mw, makeReq({method: "GET", url: "/.well-known/oauth-authorization-server/soajs/mcp/auth/default", mcp: mcp, headers: {"x-forwarded-proto": "http"}}), (answer) => {
|
|
292
|
+
assert.strictEqual(answer.body.issuer, "https://api.connect.example/soajs/mcp/auth/default");
|
|
293
|
+
run(auth.authorize({provision: provision}), makeReq({mcp: mcp, headers: {"x-forwarded-proto": "http"}}), (answer) => {
|
|
294
|
+
assert.strictEqual(answer.headers["WWW-Authenticate"],
|
|
295
|
+
'Bearer resource_metadata="https://api.connect.example/.well-known/oauth-protected-resource/soajs/mcp"');
|
|
296
|
+
done();
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
});
|
|
279
301
|
});
|
|
280
302
|
});
|