From b022f3923a075269ef3c616b80790776cac9920c Mon Sep 17 00:00:00 2001 From: blaise1030 Date: Wed, 17 Jun 2026 18:08:26 +0800 Subject: [PATCH] feat(lan): add rotating single-use invite tokens for secure LAN access (30s) - Tokens rotate every 30 seconds and are immediately invalidated after use - QR code now encodes invite URL with current token (?invite=...) - Non-loopback LAN clients must present a valid unconsumed token to authenticate - Desktop localhost access remains tokenless for convenience - Settings > Network now shows current invite link + live-updating QR - Frontend auto-detects token from URL, sends it on auth, cleans URL after use - Polling keeps the invite QR fresh while viewing settings Also includes earlier LAN binding (0.0.0.0) and mobile UI responsiveness improvements (sidebar collapse, aux panels via page layout on small screens). Tested: pnpm build succeeds, Go builds and basic flows verified. --- apps/cli/args.ts | 7 +- apps/frontend/src/api/auth.ts | 9 +- .../settings/pages/NetworkSettings.vue | 102 +++++++++- .../terminal/layout/TerminalWorkspace.vue | 28 +-- .../workspace/layout/WorkspaceLayout.vue | 10 +- apps/frontend/src/router/index.ts | 13 +- apps/server-go/internal/api/router.go | 2 +- apps/server-go/internal/appstate/state.go | 4 +- apps/server-go/internal/auth/local.go | 24 +++ apps/server-go/internal/auth/router.go | 14 +- apps/server-go/internal/cli/cli.go | 1 + apps/server-go/internal/cli/flags.go | 8 +- apps/server-go/internal/lan/lan.go | 176 +++++++++++++++++- .../internal/server/allowed_hosts_test.go | 15 +- apps/server-go/internal/server/server.go | 40 +++- apps/server-go/internal/settings/router.go | 29 ++- 16 files changed, 432 insertions(+), 50 deletions(-) diff --git a/apps/cli/args.ts b/apps/cli/args.ts index 0a77513..bfd35fb 100644 --- a/apps/cli/args.ts +++ b/apps/cli/args.ts @@ -9,6 +9,7 @@ export interface CliArgs { host: string; forceHttp: boolean; assumeYes: boolean; + lan: boolean; showHelp: boolean; } @@ -21,6 +22,7 @@ Options: -p, --port Port (default: ${DEFAULT_NETWORK_PORT}, or PORT env, or ~/.workbench/config.json) --host Local hostname (default: ${DEFAULT_NETWORK_HOST}, or WORKBENCH_HOST env) --http, --insecure Serve HTTP on localhost only (no mkcert) + --lan, --expose Bind on all interfaces and show LAN URLs -y, --yes Install mkcert without prompting if missing -h, --help Show this help @@ -36,6 +38,7 @@ export function parseCliArgs(argv: string[]): CliArgs { let host = process.env.WORKBENCH_HOST?.trim() || fileConfig.host; let forceHttp = false; let assumeYes = false; + let lan = false; let showHelp = false; for (let i = 0; i < argv.length; i++) { @@ -46,6 +49,8 @@ export function parseCliArgs(argv: string[]): CliArgs { assumeYes = true; } else if (arg === "--http" || arg === "--insecure") { forceHttp = true; + } else if (arg === "--lan" || arg === "--expose") { + lan = true; } else if (arg === "--port" || arg === "-p") { const next = argv[++i]; if (!next) throw new Error("Missing value for --port"); @@ -60,7 +65,7 @@ export function parseCliArgs(argv: string[]): CliArgs { } } - return { port, host, forceHttp, assumeYes, showHelp }; + return { port, host, forceHttp, assumeYes, lan, showHelp }; } export function printCliHelp(): void { diff --git a/apps/frontend/src/api/auth.ts b/apps/frontend/src/api/auth.ts index 47a7ca4..8a9f6f9 100644 --- a/apps/frontend/src/api/auth.ts +++ b/apps/frontend/src/api/auth.ts @@ -1,8 +1,11 @@ import { apiClient } from "@/lib/api-client"; import { ensureOk } from "@/lib/api-error"; -/** Auto-authenticate when the UI is served from the same machine as the server. */ -export async function ensureLocalAuth(): Promise { - const res = await apiClient.auth.local.$post(); +/** Auto-authenticate when the UI is served from the same machine as the server. + * If an invite token is provided (from QR), it will be sent for validation. + */ +export async function ensureLocalAuth(token?: string): Promise { + const body = token ? { token } : {}; + const res = await apiClient.auth.local.$post({ json: body }); await ensureOk<{ ok: true }>(res); } diff --git a/apps/frontend/src/modules/settings/pages/NetworkSettings.vue b/apps/frontend/src/modules/settings/pages/NetworkSettings.vue index e7856b4..c866a1b 100644 --- a/apps/frontend/src/modules/settings/pages/NetworkSettings.vue +++ b/apps/frontend/src/modules/settings/pages/NetworkSettings.vue @@ -1,7 +1,10 @@