Added OIDC Client CORS attributes
This commit is contained in:
+1
-1
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user