Add OIDC admin config page with auto-signup toggle
Some checks failed
CI / build (push) Successful in 1m12s
CI / docker (push) Successful in 50s
CI / deploy (push) Failing after 2m10s

Backend: add autoSignup field to OidcConfig, ClickHouse schema, repository,
and admin controller. Gate OIDC login when auto-signup is disabled and user
is not pre-created (returns 403).

Frontend: add OIDC admin page with full CRUD (save/test/delete), role-gated
Admin nav link parsed from JWT, and matching design system styles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-14 13:56:02 +01:00
parent 377908cc61
commit 0c47ac9b1a
12 changed files with 802 additions and 14 deletions

View File

@@ -0,0 +1,80 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { config } from '../../config';
import { useAuthStore } from '../../auth/auth-store';
export interface OidcConfigResponse {
configured: boolean;
enabled?: boolean;
issuerUri?: string;
clientId?: string;
clientSecretSet?: boolean;
rolesClaim?: string;
defaultRoles?: string[];
autoSignup?: boolean;
}
export interface OidcConfigRequest {
enabled: boolean;
issuerUri: string;
clientId: string;
clientSecret: string;
rolesClaim: string;
defaultRoles: string[];
autoSignup: boolean;
}
interface TestResult {
status: string;
authorizationEndpoint: string;
}
async function adminFetch<T>(path: string, options?: RequestInit): Promise<T> {
const token = useAuthStore.getState().accessToken;
const res = await fetch(`${config.apiBaseUrl}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
...options?.headers,
},
});
if (res.status === 204) return undefined as T;
const body = await res.json();
if (!res.ok) throw new Error(body.message || `Request failed (${res.status})`);
return body as T;
}
export function useOidcConfig() {
return useQuery<OidcConfigResponse>({
queryKey: ['admin', 'oidc'],
queryFn: () => adminFetch('/admin/oidc'),
});
}
export function useSaveOidcConfig() {
const qc = useQueryClient();
return useMutation({
mutationFn: (data: OidcConfigRequest) =>
adminFetch<OidcConfigResponse>('/admin/oidc', {
method: 'PUT',
body: JSON.stringify(data),
}),
onSuccess: () => qc.invalidateQueries({ queryKey: ['admin', 'oidc'] }),
});
}
export function useTestOidcConnection() {
return useMutation({
mutationFn: () =>
adminFetch<TestResult>('/admin/oidc/test', { method: 'POST' }),
});
}
export function useDeleteOidcConfig() {
const qc = useQueryClient();
return useMutation({
mutationFn: () =>
adminFetch<void>('/admin/oidc', { method: 'DELETE' }),
onSuccess: () => qc.invalidateQueries({ queryKey: ['admin', 'oidc'] }),
});
}