feat(auth): add local login, audit, API keys, and system admin UI

Gate netx Web/API/WebCRT with JWT and per-user API tokens, bootstrap an admin with forced password change, and expose users/audit/API-key management under a System section. MCP can reuse data/auth/mcp_token without extra env for local labs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
hansjone 2026-07-30 02:34:25 +00:00
parent 14f14d34bd
commit 6d4cd741ef
35 changed files with 2699 additions and 21 deletions

View file

@ -6,6 +6,7 @@ import { getPageTitleKey, isWorkbenchPath } from "../config/modules";
import { useAppWindowRegistration } from "../hooks/useAppWindowRegistration";
import { useI18n } from "../i18n";
import { returnToWorkbench } from "../utils/workbench";
import { useAuth } from "../auth/AuthContext";
type ConnLevel = "up" | "down" | "unknown";
@ -26,6 +27,7 @@ type Props = {
export function AppLayout({ connections, children }: Props) {
const { t } = useI18n();
const { pathname } = useLocation();
const { user, logout } = useAuth();
const onWorkbench = isWorkbenchPath(pathname);
const pageTitle = t(getPageTitleKey(pathname));
const netxSuffix =
@ -86,6 +88,20 @@ export function AppLayout({ connections, children }: Props) {
{t("layout.oclawBridge")}: {connections.oclawBridge}
{oclawSuffix}
</span>
{user ? (
<span className="conn-pill conn-pill--on-brand conn-pill--up" title={user.role}>
{user.username}
</span>
) : null}
{user ? (
<button
type="button"
className="header-menu__trigger header-menu__trigger--on-brand"
onClick={() => void logout()}
>
{t("auth.logout")}
</button>
) : null}
<HeaderMenu />
</div>
</header>