fix: preserve original host through forward auth
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -28,16 +28,16 @@
|
||||
"wrnexus": "./dist/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.21",
|
||||
"@wrnexus/router": "^0.2.21",
|
||||
"@wrnexus/csr": "^0.2.21",
|
||||
"@wrnexus/compiler": "^0.2.21",
|
||||
"@wrnexus/styles": "^0.2.21",
|
||||
"@wrnexus/dev-server": "^0.2.21",
|
||||
"@wrnexus/ui": "^0.2.21",
|
||||
"@wrnexus/validation": "^0.2.21",
|
||||
"@wrnexus/i18n": "^0.2.21",
|
||||
"@wrnexus/db": "^0.2.21"
|
||||
"@wrnexus/core": "^0.2.22",
|
||||
"@wrnexus/router": "^0.2.22",
|
||||
"@wrnexus/csr": "^0.2.22",
|
||||
"@wrnexus/compiler": "^0.2.22",
|
||||
"@wrnexus/styles": "^0.2.22",
|
||||
"@wrnexus/dev-server": "^0.2.22",
|
||||
"@wrnexus/ui": "^0.2.22",
|
||||
"@wrnexus/validation": "^0.2.22",
|
||||
"@wrnexus/i18n": "^0.2.22",
|
||||
"@wrnexus/db": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/core — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.21"
|
||||
"@wrnexus/core": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/db — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -25,18 +25,18 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.21",
|
||||
"@wrnexus/router": "^0.2.21",
|
||||
"@wrnexus/ssr": "^0.2.21",
|
||||
"@wrnexus/csr": "^0.2.21",
|
||||
"@wrnexus/compiler": "^0.2.21",
|
||||
"@wrnexus/styles": "^0.2.21",
|
||||
"@wrnexus/ui": "^0.2.21",
|
||||
"@wrnexus/validation": "^0.2.21",
|
||||
"@wrnexus/i18n": "^0.2.21",
|
||||
"@wrnexus/db": "^0.2.21",
|
||||
"@wrnexus/pubsub": "^0.2.21",
|
||||
"@wrnexus/uploader": "^0.2.21"
|
||||
"@wrnexus/core": "^0.2.22",
|
||||
"@wrnexus/router": "^0.2.22",
|
||||
"@wrnexus/ssr": "^0.2.22",
|
||||
"@wrnexus/csr": "^0.2.22",
|
||||
"@wrnexus/compiler": "^0.2.22",
|
||||
"@wrnexus/styles": "^0.2.22",
|
||||
"@wrnexus/ui": "^0.2.22",
|
||||
"@wrnexus/validation": "^0.2.22",
|
||||
"@wrnexus/i18n": "^0.2.22",
|
||||
"@wrnexus/db": "^0.2.22",
|
||||
"@wrnexus/pubsub": "^0.2.22",
|
||||
"@wrnexus/uploader": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/encryption — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -44,6 +44,11 @@ Always list the application hosts that are valid redirect destinations. Forwarde
|
||||
headers are rejected when `allowedHosts` is absent or does not match, preventing an open
|
||||
redirect. A callback can support dynamic tenant domains:
|
||||
|
||||
The SSO hostname is the login destination, not an `allowedHosts` entry. For example,
|
||||
when protecting `admin.localhost:3000`, keep `admin.localhost:3000` in the allowlist even
|
||||
though the verifier runs at `sso.localhost:3000`. WRNexus preserves both hosts across a
|
||||
nested gateway request.
|
||||
|
||||
```ts
|
||||
allowedHosts: (host) => host.endsWith(".example.test");
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.21"
|
||||
"@wrnexus/core": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/i18n — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.21"
|
||||
"@wrnexus/core": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/jwt — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/native": "^0.2.21"
|
||||
"@wrnexus/native": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/native — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/router — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,8 +21,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/compiler": "^0.2.21",
|
||||
"@wrnexus/core": "^0.2.21"
|
||||
"@wrnexus/compiler": "^0.2.22",
|
||||
"@wrnexus/core": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.21"
|
||||
"@wrnexus/core": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/uploader": "^0.2.21"
|
||||
"@wrnexus/uploader": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/test — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -22,7 +22,7 @@
|
||||
"./ui.css": "./ui.css"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.21"
|
||||
"@wrnexus/core": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/uploader — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.21"
|
||||
"@wrnexus/core": "^0.2.22"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/validation — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -272,6 +272,14 @@ const MIGRATIONS: Migration[] = [
|
||||
// Explicit no-op: updating @wrnexus/dev-server is sufficient.
|
||||
},
|
||||
},
|
||||
{
|
||||
version: "0.2.22",
|
||||
id: "nested-forward-auth-origin",
|
||||
description: "No project-file changes; nested forward-auth headers are fixed at runtime",
|
||||
apply() {
|
||||
// Explicit no-op: updating @wrnexus/dev-server and helpers is sufficient.
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/** Release tooling uses this to require an explicit migration entry per version. */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -135,11 +135,18 @@ export function forwardAuthFailure(res: Response, verifierUrl: string): Response
|
||||
/** Describe the original gateway request to a forward-auth verifier. */
|
||||
export function forwardAuthHeaders(req: Request): Headers {
|
||||
const original = new URL(req.url);
|
||||
const host = req.headers.get("host") ?? original.host;
|
||||
const protocol = original.protocol.replace(":", "");
|
||||
return new Headers({
|
||||
cookie: req.headers.get("cookie") ?? "",
|
||||
authorization: req.headers.get("authorization") ?? "",
|
||||
"x-forwarded-host": req.headers.get("host") ?? original.host,
|
||||
"x-forwarded-proto": original.protocol.replace(":", ""),
|
||||
"x-forwarded-host": host,
|
||||
"x-forwarded-proto": protocol,
|
||||
// These survive when a verifier such as sso.localhost is routed through
|
||||
// this gateway again. The second hop may replace X-Forwarded-Host with the
|
||||
// verifier host, but must not lose the protected application's return URL.
|
||||
"x-original-host": host,
|
||||
"x-original-proto": protocol,
|
||||
"x-original-method": req.method,
|
||||
"x-original-uri": `${original.pathname}${original.search}`,
|
||||
});
|
||||
|
||||
@@ -52,12 +52,37 @@ test("forward auth describes the original gateway request", () => {
|
||||
|
||||
expect(headers.get("x-forwarded-host")).toBe("admin.example.test");
|
||||
expect(headers.get("x-forwarded-proto")).toBe("https");
|
||||
expect(headers.get("x-original-host")).toBe("admin.example.test");
|
||||
expect(headers.get("x-original-proto")).toBe("https");
|
||||
expect(headers.get("x-original-method")).toBe("GET");
|
||||
expect(headers.get("x-original-uri")).toBe("/settings?tab=security");
|
||||
expect(headers.get("cookie")).toBe("session=abc");
|
||||
expect(headers.get("authorization")).toBe("Bearer token");
|
||||
});
|
||||
|
||||
test("nested SSO proxy keeps the protected app's original request headers", () => {
|
||||
const authHeaders = forwardAuthHeaders(
|
||||
new Request("http://admin.localhost:3000/settings", {
|
||||
headers: { host: "admin.localhost:3000" },
|
||||
}),
|
||||
);
|
||||
authHeaders.set("host", "sso.localhost:3000");
|
||||
const verifierRequest = new Request("http://sso.localhost:3000/api/verify", {
|
||||
headers: authHeaders,
|
||||
});
|
||||
|
||||
const proxied = gatewayProxyHeaders(
|
||||
verifierRequest,
|
||||
new URL(verifierRequest.url),
|
||||
"127.0.0.1",
|
||||
true,
|
||||
);
|
||||
|
||||
expect(proxied.get("x-forwarded-host")).toBe("sso.localhost:3000");
|
||||
expect(proxied.get("x-original-host")).toBe("admin.localhost:3000");
|
||||
expect(proxied.get("x-original-uri")).toBe("/settings");
|
||||
});
|
||||
|
||||
test("gateway respawns development apps after an HMR restart exit", () => {
|
||||
expect(gatewayRestartDelay("development", 97, null)).toBe(0);
|
||||
expect(gatewayRestartDelay("development", 1, null)).toBe(1200);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -44,6 +44,11 @@ Always list the application hosts that are valid redirect destinations. Forwarde
|
||||
headers are rejected when `allowedHosts` is absent or does not match, preventing an open
|
||||
redirect. A callback can support dynamic tenant domains:
|
||||
|
||||
The SSO hostname is the login destination, not an `allowedHosts` entry. For example,
|
||||
when protecting `admin.localhost:3000`, keep `admin.localhost:3000` in the allowlist even
|
||||
though the verifier runs at `sso.localhost:3000`. WRNexus preserves both hosts across a
|
||||
nested gateway request.
|
||||
|
||||
```ts
|
||||
allowedHosts: (host) => host.endsWith(".example.test");
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
|
||||
@@ -78,7 +78,10 @@ export function getOriginalRequestUrl(
|
||||
ctx: RequestContext,
|
||||
options: OriginalRequestOptions = {},
|
||||
): URL {
|
||||
const host = forwardedValue(ctx, "x-forwarded-host");
|
||||
// A forward-auth verifier can itself sit behind the same gateway. In that
|
||||
// nested hop X-Forwarded-Host correctly describes the verifier (SSO), while
|
||||
// X-Original-Host keeps the protected application's host (for returnTo).
|
||||
const host = forwardedValue(ctx, "x-original-host") ?? forwardedValue(ctx, "x-forwarded-host");
|
||||
const path = getOriginalRequestPath(ctx);
|
||||
|
||||
if (!host) return new URL(path, ctx.url.origin);
|
||||
@@ -86,7 +89,11 @@ export function getOriginalRequestUrl(
|
||||
throw new TypeError(`Untrusted forwarded host: ${host}`);
|
||||
}
|
||||
|
||||
const protocol = (forwardedValue(ctx, "x-forwarded-proto") ?? ctx.url.protocol).replace(/:$/, "");
|
||||
const protocol = (
|
||||
forwardedValue(ctx, "x-original-proto") ??
|
||||
forwardedValue(ctx, "x-forwarded-proto") ??
|
||||
ctx.url.protocol
|
||||
).replace(/:$/, "");
|
||||
if (protocol !== "http" && protocol !== "https") {
|
||||
throw new TypeError(`Unsupported forwarded protocol: ${protocol}`);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,28 @@ test("reconstructs an allowed original gateway URL", () => {
|
||||
expect(getOriginalRequestMethod(ctx)).toBe("GET");
|
||||
});
|
||||
|
||||
test("keeps the protected app URL when the SSO verifier crosses the gateway again", () => {
|
||||
const ctx = context("http://sso.localhost:3000/api/verify", {
|
||||
"x-forwarded-host": "sso.localhost:3000",
|
||||
"x-forwarded-proto": "http",
|
||||
"x-original-host": "admin.localhost:3000",
|
||||
"x-original-proto": "http",
|
||||
"x-original-method": "GET",
|
||||
"x-original-uri": "/settings?tab=security",
|
||||
});
|
||||
|
||||
const response = redirectToLogin(ctx, "/login", {
|
||||
allowedHosts: ["admin.localhost:3000"],
|
||||
});
|
||||
const location = new URL(response.headers.get("location")!);
|
||||
|
||||
expect(location.origin).toBe("http://sso.localhost:3000");
|
||||
expect(location.pathname).toBe("/login");
|
||||
expect(location.searchParams.get("returnTo")).toBe(
|
||||
"http://admin.localhost:3000/settings?tab=security",
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects untrusted hosts and unsafe request paths", () => {
|
||||
const untrusted = context("http://sso.localhost/api/verify", {
|
||||
"x-forwarded-host": "evil.example",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.2.21",
|
||||
"version": "0.2.22",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
Reference in New Issue
Block a user