mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 19:08:27 +00:00
refactor(gateway): make ws and http auth surfaces explicit
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { AuthRateLimiter } from "./auth-rate-limit.js";
|
import type { AuthRateLimiter } from "./auth-rate-limit.js";
|
||||||
import { authorizeGatewayConnect, resolveGatewayAuth } from "./auth.js";
|
import {
|
||||||
|
authorizeGatewayConnect,
|
||||||
|
authorizeHttpGatewayConnect,
|
||||||
|
authorizeWsControlUiGatewayConnect,
|
||||||
|
resolveGatewayAuth,
|
||||||
|
} from "./auth.js";
|
||||||
|
|
||||||
function createLimiterSpy(): AuthRateLimiter & {
|
function createLimiterSpy(): AuthRateLimiter & {
|
||||||
check: ReturnType<typeof vi.fn>;
|
check: ReturnType<typeof vi.fn>;
|
||||||
@@ -215,7 +220,7 @@ describe("gateway auth", () => {
|
|||||||
auth: { mode: "token", token: "secret", allowTailscale: true },
|
auth: { mode: "token", token: "secret", allowTailscale: true },
|
||||||
connectAuth: null,
|
connectAuth: null,
|
||||||
tailscaleWhois: async () => ({ login: "peter", name: "Peter" }),
|
tailscaleWhois: async () => ({ login: "peter", name: "Peter" }),
|
||||||
allowTailscaleHeaderAuth: true,
|
authSurface: "ws-control-ui",
|
||||||
req: {
|
req: {
|
||||||
socket: { remoteAddress: "127.0.0.1" },
|
socket: { remoteAddress: "127.0.0.1" },
|
||||||
headers: {
|
headers: {
|
||||||
@@ -234,6 +239,49 @@ describe("gateway auth", () => {
|
|||||||
expect(res.user).toBe("peter");
|
expect(res.user).toBe("peter");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps tailscale header auth disabled on HTTP auth wrapper", async () => {
|
||||||
|
const res = await authorizeHttpGatewayConnect({
|
||||||
|
auth: { mode: "token", token: "secret", allowTailscale: true },
|
||||||
|
connectAuth: null,
|
||||||
|
tailscaleWhois: async () => ({ login: "peter", name: "Peter" }),
|
||||||
|
req: {
|
||||||
|
socket: { remoteAddress: "127.0.0.1" },
|
||||||
|
headers: {
|
||||||
|
host: "gateway.local",
|
||||||
|
"x-forwarded-for": "100.64.0.1",
|
||||||
|
"x-forwarded-proto": "https",
|
||||||
|
"x-forwarded-host": "ai-hub.bone-egret.ts.net",
|
||||||
|
"tailscale-user-login": "peter",
|
||||||
|
"tailscale-user-name": "Peter",
|
||||||
|
},
|
||||||
|
} as never,
|
||||||
|
});
|
||||||
|
expect(res.ok).toBe(false);
|
||||||
|
expect(res.reason).toBe("token_missing");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("enables tailscale header auth on ws control-ui auth wrapper", async () => {
|
||||||
|
const res = await authorizeWsControlUiGatewayConnect({
|
||||||
|
auth: { mode: "token", token: "secret", allowTailscale: true },
|
||||||
|
connectAuth: null,
|
||||||
|
tailscaleWhois: async () => ({ login: "peter", name: "Peter" }),
|
||||||
|
req: {
|
||||||
|
socket: { remoteAddress: "127.0.0.1" },
|
||||||
|
headers: {
|
||||||
|
host: "gateway.local",
|
||||||
|
"x-forwarded-for": "100.64.0.1",
|
||||||
|
"x-forwarded-proto": "https",
|
||||||
|
"x-forwarded-host": "ai-hub.bone-egret.ts.net",
|
||||||
|
"tailscale-user-login": "peter",
|
||||||
|
"tailscale-user-name": "Peter",
|
||||||
|
},
|
||||||
|
} as never,
|
||||||
|
});
|
||||||
|
expect(res.ok).toBe(true);
|
||||||
|
expect(res.method).toBe("tailscale");
|
||||||
|
expect(res.user).toBe("peter");
|
||||||
|
});
|
||||||
|
|
||||||
it("uses proxy-aware request client IP by default for rate-limit checks", async () => {
|
it("uses proxy-aware request client IP by default for rate-limit checks", async () => {
|
||||||
const limiter = createLimiterSpy();
|
const limiter = createLimiterSpy();
|
||||||
const res = await authorizeGatewayConnect({
|
const res = await authorizeGatewayConnect({
|
||||||
|
|||||||
@@ -52,6 +52,27 @@ type ConnectAuth = {
|
|||||||
password?: string;
|
password?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GatewayAuthSurface = "http" | "ws-control-ui";
|
||||||
|
|
||||||
|
export type AuthorizeGatewayConnectParams = {
|
||||||
|
auth: ResolvedGatewayAuth;
|
||||||
|
connectAuth?: ConnectAuth | null;
|
||||||
|
req?: IncomingMessage;
|
||||||
|
trustedProxies?: string[];
|
||||||
|
tailscaleWhois?: TailscaleWhoisLookup;
|
||||||
|
/**
|
||||||
|
* Explicit auth surface. HTTP keeps Tailscale forwarded-header auth disabled.
|
||||||
|
* WS Control UI enables it intentionally for tokenless trusted-host login.
|
||||||
|
*/
|
||||||
|
authSurface?: GatewayAuthSurface;
|
||||||
|
/** Optional rate limiter instance; when provided, failed attempts are tracked per IP. */
|
||||||
|
rateLimiter?: AuthRateLimiter;
|
||||||
|
/** Client IP used for rate-limit tracking. Falls back to proxy-aware request IP resolution. */
|
||||||
|
clientIp?: string;
|
||||||
|
/** Optional limiter scope; defaults to shared-secret auth scope. */
|
||||||
|
rateLimitScope?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type TailscaleUser = {
|
type TailscaleUser = {
|
||||||
login: string;
|
login: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -319,27 +340,17 @@ function authorizeTrustedProxy(params: {
|
|||||||
return { user };
|
return { user };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function authorizeGatewayConnect(params: {
|
function shouldAllowTailscaleHeaderAuth(authSurface: GatewayAuthSurface): boolean {
|
||||||
auth: ResolvedGatewayAuth;
|
return authSurface === "ws-control-ui";
|
||||||
connectAuth?: ConnectAuth | null;
|
}
|
||||||
req?: IncomingMessage;
|
|
||||||
trustedProxies?: string[];
|
export async function authorizeGatewayConnect(
|
||||||
tailscaleWhois?: TailscaleWhoisLookup;
|
params: AuthorizeGatewayConnectParams,
|
||||||
/**
|
): Promise<GatewayAuthResult> {
|
||||||
* Opt-in for accepting Tailscale Serve identity headers as primary auth.
|
|
||||||
* Default is disabled for HTTP surfaces; WS connect enables this explicitly.
|
|
||||||
*/
|
|
||||||
allowTailscaleHeaderAuth?: boolean;
|
|
||||||
/** Optional rate limiter instance; when provided, failed attempts are tracked per IP. */
|
|
||||||
rateLimiter?: AuthRateLimiter;
|
|
||||||
/** Client IP used for rate-limit tracking. Falls back to proxy-aware request IP resolution. */
|
|
||||||
clientIp?: string;
|
|
||||||
/** Optional limiter scope; defaults to shared-secret auth scope. */
|
|
||||||
rateLimitScope?: string;
|
|
||||||
}): Promise<GatewayAuthResult> {
|
|
||||||
const { auth, connectAuth, req, trustedProxies } = params;
|
const { auth, connectAuth, req, trustedProxies } = params;
|
||||||
const tailscaleWhois = params.tailscaleWhois ?? readTailscaleWhoisIdentity;
|
const tailscaleWhois = params.tailscaleWhois ?? readTailscaleWhoisIdentity;
|
||||||
const allowTailscaleHeaderAuth = params.allowTailscaleHeaderAuth === true;
|
const authSurface = params.authSurface ?? "http";
|
||||||
|
const allowTailscaleHeaderAuth = shouldAllowTailscaleHeaderAuth(authSurface);
|
||||||
const localDirect = isLocalDirectRequest(req, trustedProxies);
|
const localDirect = isLocalDirectRequest(req, trustedProxies);
|
||||||
|
|
||||||
if (auth.mode === "trusted-proxy") {
|
if (auth.mode === "trusted-proxy") {
|
||||||
@@ -433,3 +444,21 @@ export async function authorizeGatewayConnect(params: {
|
|||||||
limiter?.recordFailure(ip, rateLimitScope);
|
limiter?.recordFailure(ip, rateLimitScope);
|
||||||
return { ok: false, reason: "unauthorized" };
|
return { ok: false, reason: "unauthorized" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function authorizeHttpGatewayConnect(
|
||||||
|
params: Omit<AuthorizeGatewayConnectParams, "authSurface">,
|
||||||
|
): Promise<GatewayAuthResult> {
|
||||||
|
return authorizeGatewayConnect({
|
||||||
|
...params,
|
||||||
|
authSurface: "http",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function authorizeWsControlUiGatewayConnect(
|
||||||
|
params: Omit<AuthorizeGatewayConnectParams, "authSurface">,
|
||||||
|
): Promise<GatewayAuthResult> {
|
||||||
|
return authorizeGatewayConnect({
|
||||||
|
...params,
|
||||||
|
authSurface: "ws-control-ui",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { ResolvedGatewayAuth } from "./auth.js";
|
|||||||
import { authorizeGatewayBearerRequestOrReply } from "./http-auth-helpers.js";
|
import { authorizeGatewayBearerRequestOrReply } from "./http-auth-helpers.js";
|
||||||
|
|
||||||
vi.mock("./auth.js", () => ({
|
vi.mock("./auth.js", () => ({
|
||||||
authorizeGatewayConnect: vi.fn(),
|
authorizeHttpGatewayConnect: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("./http-common.js", () => ({
|
vi.mock("./http-common.js", () => ({
|
||||||
@@ -15,7 +15,7 @@ vi.mock("./http-utils.js", () => ({
|
|||||||
getBearerToken: vi.fn(),
|
getBearerToken: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { authorizeGatewayConnect } = await import("./auth.js");
|
const { authorizeHttpGatewayConnect } = await import("./auth.js");
|
||||||
const { sendGatewayAuthFailure } = await import("./http-common.js");
|
const { sendGatewayAuthFailure } = await import("./http-common.js");
|
||||||
const { getBearerToken } = await import("./http-utils.js");
|
const { getBearerToken } = await import("./http-utils.js");
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ describe("authorizeGatewayBearerRequestOrReply", () => {
|
|||||||
|
|
||||||
it("disables tailscale header auth for HTTP bearer checks", async () => {
|
it("disables tailscale header auth for HTTP bearer checks", async () => {
|
||||||
vi.mocked(getBearerToken).mockReturnValue(undefined);
|
vi.mocked(getBearerToken).mockReturnValue(undefined);
|
||||||
vi.mocked(authorizeGatewayConnect).mockResolvedValue({
|
vi.mocked(authorizeHttpGatewayConnect).mockResolvedValue({
|
||||||
ok: false,
|
ok: false,
|
||||||
reason: "token_missing",
|
reason: "token_missing",
|
||||||
});
|
});
|
||||||
@@ -43,9 +43,8 @@ describe("authorizeGatewayBearerRequestOrReply", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(ok).toBe(false);
|
expect(ok).toBe(false);
|
||||||
expect(vi.mocked(authorizeGatewayConnect)).toHaveBeenCalledWith(
|
expect(vi.mocked(authorizeHttpGatewayConnect)).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
allowTailscaleHeaderAuth: false,
|
|
||||||
connectAuth: null,
|
connectAuth: null,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -54,7 +53,7 @@ describe("authorizeGatewayBearerRequestOrReply", () => {
|
|||||||
|
|
||||||
it("forwards bearer token and returns true on successful auth", async () => {
|
it("forwards bearer token and returns true on successful auth", async () => {
|
||||||
vi.mocked(getBearerToken).mockReturnValue("abc");
|
vi.mocked(getBearerToken).mockReturnValue("abc");
|
||||||
vi.mocked(authorizeGatewayConnect).mockResolvedValue({ ok: true, method: "token" });
|
vi.mocked(authorizeHttpGatewayConnect).mockResolvedValue({ ok: true, method: "token" });
|
||||||
|
|
||||||
const ok = await authorizeGatewayBearerRequestOrReply({
|
const ok = await authorizeGatewayBearerRequestOrReply({
|
||||||
req: {} as IncomingMessage,
|
req: {} as IncomingMessage,
|
||||||
@@ -68,9 +67,8 @@ describe("authorizeGatewayBearerRequestOrReply", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(ok).toBe(true);
|
expect(ok).toBe(true);
|
||||||
expect(vi.mocked(authorizeGatewayConnect)).toHaveBeenCalledWith(
|
expect(vi.mocked(authorizeHttpGatewayConnect)).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
allowTailscaleHeaderAuth: false,
|
|
||||||
connectAuth: { token: "abc", password: "abc" },
|
connectAuth: { token: "abc", password: "abc" },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||||
import type { AuthRateLimiter } from "./auth-rate-limit.js";
|
import type { AuthRateLimiter } from "./auth-rate-limit.js";
|
||||||
import { authorizeGatewayConnect, type ResolvedGatewayAuth } from "./auth.js";
|
import { authorizeHttpGatewayConnect, type ResolvedGatewayAuth } from "./auth.js";
|
||||||
import { sendGatewayAuthFailure } from "./http-common.js";
|
import { sendGatewayAuthFailure } from "./http-common.js";
|
||||||
import { getBearerToken } from "./http-utils.js";
|
import { getBearerToken } from "./http-utils.js";
|
||||||
|
|
||||||
@@ -12,12 +12,11 @@ export async function authorizeGatewayBearerRequestOrReply(params: {
|
|||||||
rateLimiter?: AuthRateLimiter;
|
rateLimiter?: AuthRateLimiter;
|
||||||
}): Promise<boolean> {
|
}): Promise<boolean> {
|
||||||
const token = getBearerToken(params.req);
|
const token = getBearerToken(params.req);
|
||||||
const authResult = await authorizeGatewayConnect({
|
const authResult = await authorizeHttpGatewayConnect({
|
||||||
auth: params.auth,
|
auth: params.auth,
|
||||||
connectAuth: token ? { token, password: token } : null,
|
connectAuth: token ? { token, password: token } : null,
|
||||||
req: params.req,
|
req: params.req,
|
||||||
trustedProxies: params.trustedProxies,
|
trustedProxies: params.trustedProxies,
|
||||||
allowTailscaleHeaderAuth: false,
|
|
||||||
rateLimiter: params.rateLimiter,
|
rateLimiter: params.rateLimiter,
|
||||||
});
|
});
|
||||||
if (!authResult.ok) {
|
if (!authResult.ok) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { safeEqualSecret } from "../security/secret-equal.js";
|
|||||||
import { handleSlackHttpRequest } from "../slack/http/index.js";
|
import { handleSlackHttpRequest } from "../slack/http/index.js";
|
||||||
import type { AuthRateLimiter } from "./auth-rate-limit.js";
|
import type { AuthRateLimiter } from "./auth-rate-limit.js";
|
||||||
import {
|
import {
|
||||||
authorizeGatewayConnect,
|
authorizeHttpGatewayConnect,
|
||||||
isLocalDirectRequest,
|
isLocalDirectRequest,
|
||||||
type GatewayAuthResult,
|
type GatewayAuthResult,
|
||||||
type ResolvedGatewayAuth,
|
type ResolvedGatewayAuth,
|
||||||
@@ -150,12 +150,11 @@ async function authorizeCanvasRequest(params: {
|
|||||||
let lastAuthFailure: GatewayAuthResult | null = null;
|
let lastAuthFailure: GatewayAuthResult | null = null;
|
||||||
const token = getBearerToken(req);
|
const token = getBearerToken(req);
|
||||||
if (token) {
|
if (token) {
|
||||||
const authResult = await authorizeGatewayConnect({
|
const authResult = await authorizeHttpGatewayConnect({
|
||||||
auth: { ...auth, allowTailscale: false },
|
auth: { ...auth, allowTailscale: false },
|
||||||
connectAuth: { token, password: token },
|
connectAuth: { token, password: token },
|
||||||
req,
|
req,
|
||||||
trustedProxies,
|
trustedProxies,
|
||||||
allowTailscaleHeaderAuth: false,
|
|
||||||
rateLimiter,
|
rateLimiter,
|
||||||
});
|
});
|
||||||
if (authResult.ok) {
|
if (authResult.ok) {
|
||||||
@@ -528,12 +527,11 @@ export function createGatewayHttpServer(opts: {
|
|||||||
// their own auth when exposing sensitive functionality.
|
// their own auth when exposing sensitive functionality.
|
||||||
if (requestPath.startsWith("/api/channels/")) {
|
if (requestPath.startsWith("/api/channels/")) {
|
||||||
const token = getBearerToken(req);
|
const token = getBearerToken(req);
|
||||||
const authResult = await authorizeGatewayConnect({
|
const authResult = await authorizeHttpGatewayConnect({
|
||||||
auth: resolvedAuth,
|
auth: resolvedAuth,
|
||||||
connectAuth: token ? { token, password: token } : null,
|
connectAuth: token ? { token, password: token } : null,
|
||||||
req,
|
req,
|
||||||
trustedProxies,
|
trustedProxies,
|
||||||
allowTailscaleHeaderAuth: false,
|
|
||||||
rateLimiter,
|
rateLimiter,
|
||||||
});
|
});
|
||||||
if (!authResult.ok) {
|
if (!authResult.ok) {
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ import {
|
|||||||
type AuthRateLimiter,
|
type AuthRateLimiter,
|
||||||
} from "../../auth-rate-limit.js";
|
} from "../../auth-rate-limit.js";
|
||||||
import type { GatewayAuthResult, ResolvedGatewayAuth } from "../../auth.js";
|
import type { GatewayAuthResult, ResolvedGatewayAuth } from "../../auth.js";
|
||||||
import { authorizeGatewayConnect, isLocalDirectRequest } from "../../auth.js";
|
import {
|
||||||
|
authorizeHttpGatewayConnect,
|
||||||
|
authorizeWsControlUiGatewayConnect,
|
||||||
|
isLocalDirectRequest,
|
||||||
|
} from "../../auth.js";
|
||||||
import {
|
import {
|
||||||
buildCanvasScopedHostUrl,
|
buildCanvasScopedHostUrl,
|
||||||
CANVAS_CAPABILITY_TTL_MS,
|
CANVAS_CAPABILITY_TTL_MS,
|
||||||
@@ -380,12 +384,11 @@ export function attachGatewayWsMessageHandler(params: {
|
|||||||
|
|
||||||
const resolveAuthState = async () => {
|
const resolveAuthState = async () => {
|
||||||
const hasDeviceTokenCandidate = Boolean(connectParams.auth?.token && device);
|
const hasDeviceTokenCandidate = Boolean(connectParams.auth?.token && device);
|
||||||
let nextAuthResult: GatewayAuthResult = await authorizeGatewayConnect({
|
let nextAuthResult: GatewayAuthResult = await authorizeWsControlUiGatewayConnect({
|
||||||
auth: resolvedAuth,
|
auth: resolvedAuth,
|
||||||
connectAuth: connectParams.auth,
|
connectAuth: connectParams.auth,
|
||||||
req: upgradeReq,
|
req: upgradeReq,
|
||||||
trustedProxies,
|
trustedProxies,
|
||||||
allowTailscaleHeaderAuth: true,
|
|
||||||
rateLimiter: hasDeviceTokenCandidate ? undefined : rateLimiter,
|
rateLimiter: hasDeviceTokenCandidate ? undefined : rateLimiter,
|
||||||
clientIp,
|
clientIp,
|
||||||
rateLimitScope: AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET,
|
rateLimitScope: AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET,
|
||||||
@@ -416,7 +419,7 @@ export function attachGatewayWsMessageHandler(params: {
|
|||||||
const nextAuthMethod =
|
const nextAuthMethod =
|
||||||
nextAuthResult.method ?? (resolvedAuth.mode === "password" ? "password" : "token");
|
nextAuthResult.method ?? (resolvedAuth.mode === "password" ? "password" : "token");
|
||||||
const sharedAuthResult = hasSharedAuth
|
const sharedAuthResult = hasSharedAuth
|
||||||
? await authorizeGatewayConnect({
|
? await authorizeHttpGatewayConnect({
|
||||||
auth: { ...resolvedAuth, allowTailscale: false },
|
auth: { ...resolvedAuth, allowTailscale: false },
|
||||||
connectAuth: connectParams.auth,
|
connectAuth: connectParams.auth,
|
||||||
req: upgradeReq,
|
req: upgradeReq,
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ vi.mock("../config/sessions.js", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("./auth.js", () => ({
|
vi.mock("./auth.js", () => ({
|
||||||
authorizeGatewayConnect: async () => ({ ok: true }),
|
authorizeHttpGatewayConnect: async () => ({ ok: true }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../logger.js", () => ({
|
vi.mock("../logger.js", () => ({
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { isSubagentSessionKey } from "../routing/session-key.js";
|
|||||||
import { DEFAULT_GATEWAY_HTTP_TOOL_DENY } from "../security/dangerous-tools.js";
|
import { DEFAULT_GATEWAY_HTTP_TOOL_DENY } from "../security/dangerous-tools.js";
|
||||||
import { normalizeMessageChannel } from "../utils/message-channel.js";
|
import { normalizeMessageChannel } from "../utils/message-channel.js";
|
||||||
import type { AuthRateLimiter } from "./auth-rate-limit.js";
|
import type { AuthRateLimiter } from "./auth-rate-limit.js";
|
||||||
import { authorizeGatewayConnect, type ResolvedGatewayAuth } from "./auth.js";
|
import { authorizeHttpGatewayConnect, type ResolvedGatewayAuth } from "./auth.js";
|
||||||
import {
|
import {
|
||||||
readJsonBodyOrError,
|
readJsonBodyOrError,
|
||||||
sendGatewayAuthFailure,
|
sendGatewayAuthFailure,
|
||||||
@@ -146,12 +146,11 @@ export async function handleToolsInvokeHttpRequest(
|
|||||||
|
|
||||||
const cfg = loadConfig();
|
const cfg = loadConfig();
|
||||||
const token = getBearerToken(req);
|
const token = getBearerToken(req);
|
||||||
const authResult = await authorizeGatewayConnect({
|
const authResult = await authorizeHttpGatewayConnect({
|
||||||
auth: opts.auth,
|
auth: opts.auth,
|
||||||
connectAuth: token ? { token, password: token } : null,
|
connectAuth: token ? { token, password: token } : null,
|
||||||
req,
|
req,
|
||||||
trustedProxies: opts.trustedProxies ?? cfg.gateway?.trustedProxies,
|
trustedProxies: opts.trustedProxies ?? cfg.gateway?.trustedProxies,
|
||||||
allowTailscaleHeaderAuth: false,
|
|
||||||
rateLimiter: opts.rateLimiter,
|
rateLimiter: opts.rateLimiter,
|
||||||
});
|
});
|
||||||
if (!authResult.ok) {
|
if (!authResult.ok) {
|
||||||
|
|||||||
Reference in New Issue
Block a user