mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
feat(web): protect launcher dashboard with token and SPA login (#1953)
Add token-based authentication for the Launcher's embedded Web Dashboard. - Ephemeral token generated in-memory each run (or via PICOCLAW_LAUNCHER_TOKEN env var) - HMAC-SHA256 session cookie (HttpOnly, SameSite=Lax, Secure when HTTPS) - Bearer token support for API/script access - Rate limiting on login (10 attempts/IP/min) - Referrer-Policy: no-referrer on all responses - POST-only logout with JSON content-type (CSRF-safe) - System tray "Copy dashboard token" action - Login page shows contextual help (console/tray/log file path) - Path traversal protection via path.Clean - X-Forwarded-Host/Port/Proto support for reverse proxy deployments - Full i18n support (English, Chinese) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { isLauncherLoginPathname } from "@/lib/launcher-login-path"
|
||||
|
||||
function isLauncherLoginPath(): boolean {
|
||||
if (typeof globalThis.location === "undefined") {
|
||||
return false
|
||||
}
|
||||
if (isLauncherLoginPathname(globalThis.location.pathname || "/")) {
|
||||
return true
|
||||
}
|
||||
try {
|
||||
return isLauncherLoginPathname(
|
||||
new URL(globalThis.location.href).pathname || "/",
|
||||
)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same-origin fetch that sends cookies; redirects to launcher login on 401 JSON responses.
|
||||
* Skips redirect while already on the login page to avoid reload loops (e.g. gateway poll).
|
||||
*/
|
||||
export async function launcherFetch(
|
||||
input: RequestInfo | URL,
|
||||
init?: RequestInit,
|
||||
): Promise<Response> {
|
||||
const res = await fetch(input, {
|
||||
credentials: "same-origin",
|
||||
...init,
|
||||
})
|
||||
if (res.status === 401) {
|
||||
const ct = res.headers.get("content-type") || ""
|
||||
if (
|
||||
ct.includes("application/json") &&
|
||||
typeof globalThis.location !== "undefined" &&
|
||||
!isLauncherLoginPath()
|
||||
) {
|
||||
globalThis.location.assign("/launcher-login")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
Reference in New Issue
Block a user