Chore: Rebranding Gatehouse to Secuird (UI)

This commit is contained in:
2026-03-06 00:22:57 +05:45
parent 7be6415db1
commit 979b5a918e
27 changed files with 76 additions and 76 deletions
+2 -2
View File
@@ -3,7 +3,7 @@ import { useSearchParams, useNavigate } from "react-router-dom";
import { CheckCircle, XCircle, Loader2, Mail } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { GatehouseLogo } from "@/components/branding/GatehouseLogo";
import { SecuirdLogo } from "@/components/branding/SecuirdLogo";
import { api, ApiError } from "@/lib/api";
type Status = "loading" | "success" | "error" | "missing";
@@ -42,7 +42,7 @@ export default function ActivatePage() {
<div className="w-full max-w-md space-y-6">
{/* Logo */}
<div className="flex justify-center">
<GatehouseLogo size="md" />
<SecuirdLogo size="md" />
</div>
<Card>
+2 -2
View File
@@ -61,7 +61,7 @@ export default function InviteAcceptPage() {
const result = await api.invites.accept(token, name || undefined, inviteData?.user_exists ? undefined : password);
if (result.token) {
// Store the token manually since we're not using the normal login flow
localStorage.setItem("gatehouse_token", result.token);
localStorage.setItem("secuird_token", result.token);
}
navigate("/profile");
} catch (err: unknown) {
@@ -127,7 +127,7 @@ export default function InviteAcceptPage() {
<CheckCircle className="w-5 h-5 text-accent flex-shrink-0 mt-0.5" />
<div className="text-sm">
<p className="font-medium text-foreground">Account found</p>
<p className="text-muted-foreground">You already have a Gatehouse account. Click below to join the organization.</p>
<p className="text-muted-foreground">You already have a Secuird account. Click below to join the organization.</p>
</div>
</div>
) : (
+14 -14
View File
@@ -27,8 +27,8 @@ import { OAuthProvider } from "@/lib/oauth";
type LoginStep = 'credentials' | 'totp' | 'webauthn' | 'passkey-email' | 'mfa-enrollment' | 'mfa';
const GATEHOUSE_API = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:5000/api/v1';
const GATEHOUSE_OIDC = GATEHOUSE_API.replace(/\/api\/v1\/?$/, '');
const SECUIRD_API = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:5000/api/v1';
const SECUIRD_OIDC = SECUIRD_API.replace(/\/api\/v1\/?$/, '');
/**
* Complete an OIDC authorization flow after the user has authenticated.
@@ -36,7 +36,7 @@ const GATEHOUSE_OIDC = GATEHOUSE_API.replace(/\/api\/v1\/?$/, '');
* the auth code and returns the redirect URL for the calling application.
*/
async function completeOidcFlow(oidcSessionId: string, token: string): Promise<string> {
const res = await fetch(`${GATEHOUSE_OIDC}/oidc/complete`, {
const res = await fetch(`${SECUIRD_OIDC}/oidc/complete`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ oidc_session_id: oidcSessionId, token }),
@@ -64,13 +64,13 @@ export default function LoginPage() {
const [mfaToken, setMfaToken] = useState<string | null>(null);
// OIDC bridge: if oidc_session_id is in the URL, we're acting as the
// login UI for an OIDC authorization flow (e.g. SecuIRD → Gatehouse).
// login UI for an OIDC authorization flow (e.g. SecuIRD → Secuird).
// After successful login, call /oidc/complete and redirect to the client app.
const oidcSessionId = searchParams.get('oidc_session_id');
const oidcError = searchParams.get('error');
// CLI bridge: if cli_token or cli_redirect is present the login was triggered
// by the Gatehouse CLI tool. After successful auth the token is delivered
// by the Secuird CLI tool. After successful auth the token is delivered
// directly to the CLI's local callback server.
const cliToken = searchParams.get('cli_token');
const cliRedirectParam = searchParams.get('cli_redirect');
@@ -81,7 +81,7 @@ export default function LoginPage() {
useEffect(() => {
if (!cliToken || cliFetchedRef.current) return;
cliFetchedRef.current = true;
fetch(`${GATEHOUSE_API}/cli/redirect-url?token=${encodeURIComponent(cliToken)}`)
fetch(`${SECUIRD_API}/cli/redirect-url?token=${encodeURIComponent(cliToken)}`)
.then((r) => r.json())
.then((body) => {
if (body?.data?.redirect_url) {
@@ -165,7 +165,7 @@ export default function LoginPage() {
// MFA enrollment required - will be handled by ProtectedLayout
// Navigation happens in AuthContext (MFA path always navigates)
} else if (oidcSessionId) {
// OIDC bridge: send token back to the Gatehouse backend to complete the flow
// OIDC bridge: send token back to the Secuird backend to complete the flow
const token = tokenManager.getToken();
if (token) await finishOidcFlow(token);
} else if (cliRedirectUrl) {
@@ -176,7 +176,7 @@ export default function LoginPage() {
// Normal login: navigation already handled by AuthContext (skipNavigate=false)
} catch (error) {
if (import.meta.env.DEV) {
console.error("[Gatehouse] Login failed:", error);
console.error("[Secuird] Login failed:", error);
}
const message = error instanceof ApiError
@@ -246,7 +246,7 @@ export default function LoginPage() {
}
} catch (error) {
if (import.meta.env.DEV) {
console.error("[Gatehouse] MFA verification failed:", error);
console.error("[Secuird] MFA verification failed:", error);
}
const message = error instanceof ApiError
@@ -294,7 +294,7 @@ export default function LoginPage() {
// Normal login: navigation already handled by AuthContext (skipNavigate=false)
} catch (error) {
if (import.meta.env.DEV) {
console.error("[Gatehouse] TOTP verification failed:", error);
console.error("[Secuird] TOTP verification failed:", error);
}
const message = error instanceof ApiError
@@ -363,7 +363,7 @@ export default function LoginPage() {
});
} catch (error) {
if (import.meta.env.DEV) {
console.error("[Gatehouse] Passkey login failed:", error);
console.error("[Secuird] Passkey login failed:", error);
}
let message = "Failed to sign in with passkey";
@@ -438,7 +438,7 @@ export default function LoginPage() {
}
} catch (error) {
if (import.meta.env.DEV) {
console.error("[Gatehouse] WebAuthn verification failed:", error);
console.error("[Secuird] WebAuthn verification failed:", error);
}
let message = "Failed to verify passkey";
@@ -518,7 +518,7 @@ export default function LoginPage() {
} catch (error) {
if (import.meta.env.DEV) {
console.error("[Gatehouse] OAuth login failed:", error);
console.error("[Secuird] OAuth login failed:", error);
}
let message = `Failed to initiate ${provider} sign in`;
@@ -939,7 +939,7 @@ export default function LoginPage() {
</h1>
<p className="text-muted-foreground mt-2">
{cliRedirectUrl
? "Sign in to grant the Gatehouse CLI access to your account"
? "Sign in to grant the Secuird CLI access to your account"
: oidcSessionId
? "An application is requesting access to your account"
: "Sign in to your account to continue"}
+6 -6
View File
@@ -9,11 +9,11 @@ import { useToast } from "@/hooks/use-toast";
type CallbackState = 'loading' | 'success' | 'error';
const GATEHOUSE_API = (import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:5000/api/v1') as string;
const GATEHOUSE_OIDC = GATEHOUSE_API.replace(/\/api\/v1\/?$/, '');
const SECUIRD_API = (import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:5000/api/v1') as string;
const SECUIRD_OIDC = SECUIRD_API.replace(/\/api\/v1\/?$/, '');
async function completeOidcFlow(oidcSessionId: string, token: string): Promise<string> {
const res = await fetch(`${GATEHOUSE_OIDC}/oidc/complete`, {
const res = await fetch(`${SECUIRD_OIDC}/oidc/complete`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ oidc_session_id: oidcSessionId, token }),
@@ -24,7 +24,7 @@ async function completeOidcFlow(oidcSessionId: string, token: string): Promise<s
}
/**
* OAuth callback page that handles the redirect from the Gatehouse backend
* OAuth callback page that handles the redirect from the Secuird backend
* after a successful (or failed) OAuth provider authentication.
*
* The backend exchanges the provider code for a session token and then
@@ -134,7 +134,7 @@ export default function OAuthCallbackPage() {
return;
} catch (oidcErr) {
if (import.meta.env.DEV) {
console.error("[Gatehouse] OIDC completion failed after OAuth:", oidcErr);
console.error("[Secuird] OIDC completion failed after OAuth:", oidcErr);
}
// Fall through to normal flow on failure — user is still logged in
}
@@ -163,7 +163,7 @@ export default function OAuthCallbackPage() {
setStatus('error');
setError("Failed to load your profile. Please try signing in again.");
if (import.meta.env.DEV) {
console.error("[Gatehouse] OAuth callback refreshUser failed:", err);
console.error("[Secuird] OAuth callback refreshUser failed:", err);
}
}
};
+4 -4
View File
@@ -6,8 +6,8 @@ import { Card } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import { tokenManager } from "@/lib/api";
const GATEHOUSE_API = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:5000/api/v1';
const GATEHOUSE_OIDC = GATEHOUSE_API.replace(/\/api\/v1\/?$/, '');
const SECUIRD_API = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:5000/api/v1';
const SECUIRD_OIDC = SECUIRD_API.replace(/\/api\/v1\/?$/, '');
const SCOPE_META: Record<string, { icon: typeof Shield; label: string; description: string }> = {
openid: { icon: Shield, label: "OpenID", description: "Verify your identity" },
@@ -41,7 +41,7 @@ export default function OIDCConsentPage() {
(async () => {
try {
const res = await fetch(`${GATEHOUSE_OIDC}/oidc/begin`, {
const res = await fetch(`${SECUIRD_OIDC}/oidc/begin`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ oidc_session_id: oidcSessionId }),
@@ -67,7 +67,7 @@ export default function OIDCConsentPage() {
navigate(`/login?oidc_session_id=${context.oidc_session_id}`);
return;
}
const res = await fetch(`${GATEHOUSE_OIDC}/oidc/complete`, {
const res = await fetch(`${SECUIRD_OIDC}/oidc/complete`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ oidc_session_id: context.oidc_session_id, token }),
+5 -5
View File
@@ -1,7 +1,7 @@
/**
* OIDCLoginPage — Standalone OIDC proxy login UI
*
* Unified entry point for OIDC authorization flows via the Gatehouse OIDC bridge.
* Unified entry point for OIDC authorization flows via the Secuird OIDC bridge.
* Handles:
* 1. Unauthenticated users → shows an email/password login form
* 2. Already-authenticated users → shows a consent/approval screen directly
@@ -9,7 +9,7 @@
* Route: /oidc-login?oidc_session_id=<id>
*
* Configure your oauth2-proxy / OIDC client's login_url to:
* https://<gatehouse-ui>/oidc-login
* https://<secuird-ui>/oidc-login
*/
import { useState, useEffect, useCallback } from "react";
import { useSearchParams, useNavigate } from "react-router-dom";
@@ -37,7 +37,7 @@ import { useAuth } from "@/contexts/AuthContext";
import { ApiError, tokenManager } from "@/lib/api";
// ── Configuration ─────────────────────────────────────────────────────────────
const GATEHOUSE_OIDC = (import.meta.env.VITE_API_BASE_URL ?? "http://localhost:5000/api/v1")
const SECUIRD_OIDC = (import.meta.env.VITE_API_BASE_URL ?? "http://localhost:5000/api/v1")
.replace(/\/api\/v1\/?$/, "");
// ── Scope display metadata ────────────────────────────────────────────────────
@@ -62,7 +62,7 @@ type PageStep = "loading" | "login" | "consent" | "error";
// ── API helpers ───────────────────────────────────────────────────────────────
async function fetchOIDCContext(oidcSessionId: string): Promise<OIDCContext> {
const res = await fetch(`${GATEHOUSE_OIDC}/oidc/begin`, {
const res = await fetch(`${SECUIRD_OIDC}/oidc/begin`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ oidc_session_id: oidcSessionId }),
@@ -75,7 +75,7 @@ async function fetchOIDCContext(oidcSessionId: string): Promise<OIDCContext> {
}
async function completeOIDCFlow(oidcSessionId: string, token: string): Promise<string> {
const res = await fetch(`${GATEHOUSE_OIDC}/oidc/complete`, {
const res = await fetch(`${SECUIRD_OIDC}/oidc/complete`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ oidc_session_id: oidcSessionId, token }),
+1 -1
View File
@@ -108,7 +108,7 @@ export default function RegisterPage() {
Create your account
</h1>
<p className="text-muted-foreground mt-2">
Get started with Gatehouse in seconds
Get started with Secuird in seconds
</p>
</div>
+1 -1
View File
@@ -261,7 +261,7 @@ export default function CAsPage() {
<div>
<h1 className="page-title">Certificate Authorities</h1>
<p className="page-description">
Manage your organization's SSH CAs with <code>Gatehouse</code>
Manage your organization's SSH CAs with <code>Secuird</code>
</p>
</div>
</div>
+3 -3
View File
@@ -160,7 +160,7 @@ export default function OIDCClientsPage() {
<div className="page-header flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<h1 className="page-title">OIDC Clients</h1>
<p className="page-description">Applications that authenticate via Gatehouse</p>
<p className="page-description">Applications that authenticate via Secuird</p>
</div>
<Button onClick={() => setDialogMode("generic")}>
<Plus className="w-4 h-4 mr-2" />
@@ -235,7 +235,7 @@ export default function OIDCClientsPage() {
<Network className="w-10 h-10 text-muted-foreground/40" />
<div>
<p className="font-medium text-muted-foreground">No OIDC clients yet</p>
<p className="text-sm text-muted-foreground/70">Register an app to let it authenticate via Gatehouse</p>
<p className="text-sm text-muted-foreground/70">Register an app to let it authenticate via Secuird</p>
</div>
<div className="flex gap-2 flex-wrap justify-center">
<Button variant="outline" onClick={() => setDialogMode("generic")}>
@@ -320,7 +320,7 @@ export default function OIDCClientsPage() {
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>Add OIDC Client</DialogTitle>
<DialogDescription>Register an application to authenticate via Gatehouse</DialogDescription>
<DialogDescription>Register an application to authenticate via Secuird</DialogDescription>
</DialogHeader>
<Tabs
+5 -5
View File
@@ -47,7 +47,7 @@ export function CADetailCard({ ca, onEdit, onRotate, onDelete }: CADetailCardPro
const isSystem = !!ca.is_system;
// ── User CA: server trusts this public key so it accepts user certs ──────
const userCaServerSnippet = `# On each SSH server — trust Gatehouse-issued user certificates:
const userCaServerSnippet = `# On each SSH server — trust Secuird-issued user certificates:
echo '${ca.public_key.trim()}' >> /etc/ssh/trusted_user_ca_keys
# /etc/ssh/sshd_config (add once, then reload sshd):
@@ -63,7 +63,7 @@ AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
# ─── Server side (separate step) ────────────────────────────────────────────
# 1. Collect the server's HOST public key:
# cat /etc/ssh/ssh_host_ed25519_key.pub
# 2. Submit it to Gatehouse → "Issue Host Certificate" to get a signed cert.
# 2. Submit it to Secuird → "Issue Host Certificate" to get a signed cert.
# 3. Install the cert on the server:
# /etc/ssh/sshd_config:
# HostKey /etc/ssh/ssh_host_ed25519_key
@@ -200,8 +200,8 @@ AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
<span className="flex items-center gap-1.5">
<Terminal className="w-3.5 h-3.5" />
{isUser
? "Server setup — trust Gatehouse user certificates"
: "Client setup — trust Gatehouse host certificates"}
? "Server setup — trust Secuird user certificates"
: "Client setup — trust Secuird host certificates"}
</span>
</AccordionTrigger>
<AccordionContent className="pb-3">
@@ -209,7 +209,7 @@ AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
<div className="mb-2 rounded border border-amber-300 dark:border-amber-700 bg-amber-50 dark:bg-amber-950/40 px-2 py-1.5 text-xs text-amber-800 dark:text-amber-300">
<strong>Two separate steps:</strong> (1) Put this CA public key in client{" "}
<code className="font-mono">known_hosts</code>. (2) Issue a host certificate
for each server via Gatehouse and install it as{" "}
for each server via Secuird and install it as{" "}
<code className="font-mono">HostCertificate</code>.
</div>
)}
+1 -1
View File
@@ -127,7 +127,7 @@ export function CASection({
<p>
Certificates are being signed by a CA key loaded from the server
configuration, not managed through this UI. Generate a managed key below to
take full control of certificate issuance from Gatehouse.
take full control of certificate issuance from Secuird.
</p>
</div>
<Button
+1 -1
View File
@@ -176,7 +176,7 @@ ssh-keygen -L -f /etc/ssh/ssh_host_ed25519_key-cert.pub`
</p>
<p>
<strong>Step 2 (here):</strong> For each server, collect its host public key,
paste it below, and Gatehouse will sign it. Install the resulting certificate
paste it below, and Secuird will sign it. Install the resulting certificate
as <code className="font-mono">HostCertificate</code> in{" "}
<code className="font-mono">sshd_config</code>.
</p>
+1 -1
View File
@@ -172,7 +172,7 @@ export default function LinkedAccountsPage() {
<Alert className="mb-6">
<AlertCircle className="w-4 h-4" />
<AlertDescription>
Linked accounts can only be used to sign in to an existing Gatehouse account.
Linked accounts can only be used to sign in to an existing Secuird account.
They cannot be used to create new accounts.
</AlertDescription>
</Alert>
+2 -2
View File
@@ -658,7 +658,7 @@ export default function SSHKeysPage() {
CA Public Key
</CardTitle>
<CardDescription>
Add this key to <code>TrustedUserCAKeys</code> on your servers so they accept certificates issued by Gatehouse.
Add this key to <code>TrustedUserCAKeys</code> on your servers so they accept certificates issued by Secuird.
</CardDescription>
</CardHeader>
<CardContent>
@@ -821,7 +821,7 @@ TrustedUserCAKeys /etc/ssh/trusted_user_ca_keys`}
{`echo '<challenge_text>' > /tmp/challenge.txt
ssh-keygen -Y sign \\
-f ~/.ssh/id_ed25519 \\
-n gatehouse \\
-n secuird \\
/tmp/challenge.txt
cat /tmp/challenge.txt.sig | base64 -w0`}
</pre>