Merge branch 'main' of github.com:CoryHawkless/gatehouse-ui

This commit is contained in:
2026-05-28 06:00:13 +00:00
3 changed files with 60 additions and 8 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ services:
build:
context: .
args:
- VITE_API_BASE_URL=https://secuird.tech/api/v1
- VITE_API_BASE_URL=https://secuird.tech/
container_name: gatehouse-ui
env_file:
- .env
+5 -4
View File
@@ -1184,10 +1184,10 @@ export const api = {
request<{ clients: OIDCClient[]; count: number }>(`/organizations/${orgId}/clients`, {}, true, requestConfig),
// Create OIDC client
createClient: (orgId: string, name: string, redirect_uris: string[], requestConfig?: RequestConfig) =>
createClient: (orgId: string, name: string, redirect_uris: string[], allowed_cors_origins?: string[] | null, requestConfig?: RequestConfig) =>
request<{ client: OIDCClientWithSecret }>(`/organizations/${orgId}/clients`, {
method: 'POST',
body: JSON.stringify({ name, redirect_uris }),
body: JSON.stringify({ name, redirect_uris, allowed_cors_origins }),
}, true, requestConfig),
// Delete OIDC client
@@ -1196,8 +1196,8 @@ export const api = {
method: 'DELETE',
}, true, requestConfig),
// Update OIDC client (name and/or redirect_uris)
updateClient: (orgId: string, clientId: string, data: { name?: string; redirect_uris?: string[] }, requestConfig?: RequestConfig) =>
// Update OIDC client (name, redirect_uris, and/or allowed_cors_origins)
updateClient: (orgId: string, clientId: string, data: { name?: string; redirect_uris?: string[]; allowed_cors_origins?: string[] | null }, requestConfig?: RequestConfig) =>
request<{ client: OIDCClient }>(`/organizations/${orgId}/clients/${clientId}`, {
method: 'PATCH',
body: JSON.stringify(data),
@@ -1854,6 +1854,7 @@ export interface OIDCClient {
redirect_uris: string[];
scopes: string[];
grant_types: string[];
allowed_cors_origins: string[] | null;
is_active: boolean;
created_at: string;
}
+54 -3
View File
@@ -119,6 +119,7 @@ export default function OIDCClientsPage() {
// Generic form
const nameRef = useRef<HTMLInputElement>(null);
const urisRef = useRef<HTMLTextAreaElement>(null);
const corsRef = useRef<HTMLTextAreaElement>(null);
// Proxy form
const proxyNameRef = useRef<HTMLInputElement>(null);
@@ -131,6 +132,7 @@ export default function OIDCClientsPage() {
const [editingClient, setEditingClient] = useState<OIDCClient | null>(null);
const [editName, setEditName] = useState("");
const [editUris, setEditUris] = useState("");
const [editCors, setEditCors] = useState("");
const [isSavingEdit, setIsSavingEdit] = useState(false);
useEffect(() => {
@@ -149,10 +151,16 @@ export default function OIDCClientsPage() {
let uris: string[];
let proxyHost: string | undefined;
let corsOrigins: string[] | null = null;
if (dialogMode === "generic") {
name = nameRef.current?.value.trim() ?? "";
uris = (urisRef.current?.value ?? "").split(/[\n,]+/).map((u) => u.trim()).filter(Boolean);
if (!name || !uris.length) return;
const corsRaw = (corsRef.current?.value ?? "").trim();
if (corsRaw) {
corsOrigins = corsRaw.split(/[\n,]+/).map((o) => o.trim()).filter(Boolean);
}
} else {
name = proxyNameRef.current?.value.trim() ?? "";
proxyHost = proxyHostRef.current?.value.trim() ?? "";
@@ -166,7 +174,7 @@ export default function OIDCClientsPage() {
setIsCreating(true);
try {
const result = await api.organizations.createClient(orgId, name, uris);
const result = await api.organizations.createClient(orgId, name, uris, corsOrigins);
const created = result.client as OIDCClientWithSecret;
setClients((prev) => [...prev, created]);
setNewSecret({
@@ -202,6 +210,7 @@ export default function OIDCClientsPage() {
setEditingClient(client);
setEditName(client.name);
setEditUris((client.redirect_uris ?? []).join("\n"));
setEditCors((client.allowed_cors_origins ?? []).join("\n"));
};
const handleSaveEdit = async () => {
@@ -210,9 +219,14 @@ export default function OIDCClientsPage() {
const uris = editUris.split(/[\n,]+/).map((u) => u.trim()).filter(Boolean);
if (!name || !uris.length) return;
const corsRaw = editCors.trim();
const corsOrigins: string[] | null = corsRaw
? corsRaw.split(/[\n,]+/).map((o) => o.trim()).filter(Boolean)
: null;
setIsSavingEdit(true);
try {
const result = await api.organizations.updateClient(orgId, editingClient.id, { name, redirect_uris: uris });
const result = await api.organizations.updateClient(orgId, editingClient.id, { name, redirect_uris: uris, allowed_cors_origins: corsOrigins });
setClients((prev) =>
prev.map((c) => (c.id === editingClient.id ? result.client : c))
);
@@ -390,6 +404,16 @@ export default function OIDCClientsPage() {
</DropdownMenuContent>
</DropdownMenu>
</div>
{client.allowed_cors_origins && client.allowed_cors_origins.length > 0 && (
<div className="flex flex-wrap gap-1 mt-2">
<span className="text-xs text-muted-foreground mr-1">CORS:</span>
{client.allowed_cors_origins.map((origin) => (
<Badge key={origin} variant="outline" className="text-xs font-mono">
{origin}
</Badge>
))}
</div>
)}
<div className="mt-3 pt-3 border-t flex items-center justify-between text-xs text-muted-foreground">
<span>Created {new Date(client.created_at).toLocaleDateString()}</span>
<span>
@@ -439,6 +463,19 @@ export default function OIDCClientsPage() {
/>
<p className="text-xs text-muted-foreground">One URI per line</p>
</div>
<div className="space-y-2">
<Label htmlFor="corsOrigins">Allowed CORS origins</Label>
<Textarea
id="corsOrigins"
placeholder={"https://myapp.example.com\nhttps://staging.myapp.example.com"}
className="min-h-[60px] font-mono text-sm"
ref={corsRef}
/>
<p className="text-xs text-muted-foreground">
One origin per line (scheme + host + optional port, no path). Leave empty to use the server default.
Use <code className="bg-muted px-1 rounded">+</code> to auto-derive from redirect URIs, or <code className="bg-muted px-1 rounded">*</code> to allow any origin.
</p>
</div>
</TabsContent>
{/* oauth2-proxy tab */}
@@ -521,7 +558,7 @@ export default function OIDCClientsPage() {
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>Edit OIDC Client</DialogTitle>
<DialogDescription>Update the client name and redirect URIs.</DialogDescription>
<DialogDescription>Update the client name, redirect URIs, and CORS origins.</DialogDescription>
</DialogHeader>
<div className="space-y-4 pt-2">
<div className="space-y-2">
@@ -544,6 +581,20 @@ export default function OIDCClientsPage() {
/>
<p className="text-xs text-muted-foreground">One URI per line</p>
</div>
<div className="space-y-2">
<Label htmlFor="editCors">Allowed CORS origins</Label>
<Textarea
id="editCors"
value={editCors}
onChange={(e) => setEditCors(e.target.value)}
placeholder={"https://myapp.example.com\nhttps://staging.myapp.example.com"}
className="min-h-[60px] font-mono text-sm"
/>
<p className="text-xs text-muted-foreground">
One origin per line. Leave empty to use the server default.
Use <code className="bg-muted px-1 rounded">+</code> to auto-derive from redirect URIs, or <code className="bg-muted px-1 rounded">*</code> to allow any origin.
</p>
</div>
{editingClient && (
<div className="rounded-md bg-muted/50 border px-3 py-2 space-y-1">
<p className="text-xs text-muted-foreground font-medium">Client ID (read-only)</p>