adminFetch called logout() directly on 401/403 responses, which cleared
roles and caused RequireAdmin to redirect to /exchanges while users were
editing forms. Now adminFetch attempts a token refresh before failing,
and RequireAdmin tolerates a transient empty-roles state during refresh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update @cameleer/design-system to v0.1.38 (12px minimum font size).
Replace all 10px and 11px font sizes with 12px across 25 CSS modules
and 5 TSX inline styles to match the new DS floor.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend holds client_secret and does the token exchange server-side,
making PKCE redundant. Removes code_verifier/code_challenge from all
frontend auth paths and backend exchange method. Eliminates the source
of "grant request is invalid" errors from verifier mismatches.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The OIDC login flow now reads roles from the access_token (JWT) in
addition to the id_token. This fixes role extraction with providers
like Logto that put scopes/roles in access tokens rather than id_tokens.
- Add audience and additionalScopes to OidcConfig for RFC 8707 resource
indicator support and configurable extra scopes
- OidcTokenExchanger decodes access_token with at+jwt-compatible processor,
falls back to id_token if access_token is opaque or has no roles
- syncOidcRoles preserves existing local roles when OIDC returns none
- SPA includes resource and additionalScopes in authorization requests
- Admin UI exposes new config fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fire end-session via fetch(no-cors) instead of window.location redirect.
Always navigate to /login?local regardless of whether end-session
succeeds, preventing broken JSON responses from blocking logout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace PNG favicons and brand logos with cameleer3-logo.svg from
@cameleer/design-system/assets. Favicon, login dialog, and sidebar
all use the same SVG. Remove PNG favicon files from public/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DS now exports ./assets/* — import PNGs directly via Vite instead of
copying to public/. Removes duplicated brand files from public/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The @cameleer/design-system package.json exports field doesn't include
assets/, causing production build failures. Copy PNGs to public/ and
reference via basePath until DS adds asset exports.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Import PNGs via Vite from @cameleer/design-system/assets instead of
copying to public/. Only favicons remain in public/ (needed by HTML).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SVG uses fill=currentColor (inherits text color). Switch to the
full-color PNG brand icons: 192px for login dialog, 48px for sidebar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hand-crafted favicon.svg with official brand assets from
@cameleer/design-system v0.1.32: PNG favicons (16/32px) and
camel-logo.svg for login dialog and sidebar. Update SecurityConfig
public endpoints accordingly. Update documentation for architecture
cleanup (PKCE, OidcProviderHelper, role normalization, K8s hardening,
Dockerfile credential removal, CI deduplication, sidebar path fix).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract OidcProviderHelper for shared discovery + JWK source construction
- Add SystemRole.normalizeScope() to centralize role normalization
- Merge duplicate claim extraction in OidcTokenExchanger
- Add PKCE (S256) to OIDC authorization flow (frontend + backend)
- Add SecurityContext (runAsNonRoot) to all K8s deployments
- Fix postgres probe to use $POSTGRES_USER instead of hardcoded username
- Remove default credentials from Dockerfile
- Extract sanitize_branch() to shared .gitea/sanitize-branch.sh
- Fix sidebar to use /exchanges/ paths directly, remove legacy redirects
- Centralize basePath computation in router.tsx via config module
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logout now always redirects to /login?local, either via OIDC
end_session or as a direct fallback, preventing prompt=none
auto-redirect from logging the user back in immediately.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Hide Admin sidebar section for non-ADMIN users
- Add RequireAdmin route guard — /admin/* redirects to / for non-admin
- Move App Config from admin section to main Config tab (per-app,
visible when app selected). VIEWER sees read-only, OPERATOR+ can edit
- Hide diagram node toolbar for VIEWER (onNodeAction conditional)
- Add useIsAdmin/useCanControl helpers to centralize role checks
- Remove App Config from admin sidebar tree
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without BASE_PATH the redirect fails behind a reverse proxy. Adding
?local prevents the SSO auto-redirect from immediately signing the
user back in after logout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When prompt=none fails with consent_required (scopes not yet granted),
retry the OIDC flow without prompt=none so the user can grant consent
once. Uses sessionStorage flag to prevent infinite loops — falls back
to local login if the retry also fails.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When OIDC is configured, the login page automatically redirects to the
provider with prompt=none. If the user has an active OIDC session, they
are signed in without seeing a login page. If the provider returns
login_required (no session), falls back to the login form via ?local.
Users can bypass auto-redirect with /login?local.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hardcoded /favicon.svg paths skip the <base> tag and fail when served
from a subpath like /server/. Now uses config.basePath in TSX and a
relative href in index.html so the <base> tag resolves correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Behind a reverse proxy with strip-prefix (e.g., Traefik at /server/),
the OIDC redirect_uri must include the prefix so the callback routes
back through the proxy. Now uses config.basePath (from <base href>)
instead of hardcoding '/'.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
25 rotating cameleer-themed login subtitles picked randomly on each
page load. Also adds the camel logo SVG next to the app name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline styles with CSS module matching the design system's
LoginForm visual patterns. Uses proper DS class structure (divider,
social section, form fields) while keeping username-based auth
instead of the DS component's email validation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Return the OIDC id_token in the callback response so the frontend can
store it and pass it as id_token_hint to the provider's end-session
endpoint on logout. This lets Authentik (or any OIDC provider) honor
the post_logout_redirect_uri and redirect back to the Cameleer login
page instead of showing the provider's own logout page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add displayName field to AuthTokenResponse so the UI shows human-readable
names instead of internal JWT subjects (e.g. user:oidc:<hash>)
- Add displayNameClaim to OIDC config (default: "name") allowing admins to
configure which ID token claim contains the user's display name
- Support dot-separated claim paths (e.g. profile.display_name) like rolesClaim
- Add admin UI field for Display Name Claim on the OIDC config page
- ClickHouse migration: ALTER TABLE adds display_name_claim column
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add dedicated request/response DTOs for all controllers, replacing raw
JsonNode parameters with validated types. Move OpenAPI path-prefix stripping
and ProcessorNode children injection into OpenApiCustomizer beans so the
spec served at /api/v1/api-docs is already clean — eliminating the need for
the ui/scripts/process-openapi.mjs post-processing script.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend:
- Expose end_session_endpoint from OIDC provider metadata in /auth/oidc/config
- Add getEndSessionEndpoint() to OidcTokenExchanger
Frontend:
- On OIDC logout, redirect to provider's end_session_endpoint to clear SSO session
- Strip /api/v1 prefix from OpenAPI paths to match client baseUrl convention
- Add schema-types.ts with convenience type re-exports from generated schema
- Fix all type imports to use schema-types instead of raw generated schema
- Fix optional field access (processors, children, duration) with proper typing
- Fix AgentInstance.state → status field name
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add "Sign in with SSO" button on login page (shown when OIDC is configured)
- Add /oidc/callback route to exchange authorization code for JWT tokens
- Add loginWithOidcCode action to auth store
- Treat issuer URI as complete discovery URL (no auto-append of .well-known)
- Update admin page placeholder to show full discovery URL format
- Fix datetime picker calendar icon visibility in dark mode (color-scheme)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
The auth store loads tokens from localStorage synchronously at import
time, but configureAuth() was deferred to a useEffect — so the first
API requests fired before the token getter was wired, causing 401s on
hard refresh. Now getAccessToken reads from the store by default.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Scaffold Vite + React + TypeScript frontend in ui/ with full design
system (dark/light themes) matching the HTML mockups
- Implement Execution Explorer page: search filters, results table with
expandable processor tree and exchange detail sidebar, pagination
- Add UI authentication: UiAuthController (login/refresh endpoints),
JWT filter handles ui: subject prefix, CORS configuration
- Shared components: StatusPill, DurationBar, StatCard, AppBadge,
FilterChip, Pagination — all using CSS Modules with design tokens
- API client layer: openapi-fetch with auth middleware, TanStack Query
hooks for search/detail/snapshot queries, Zustand for state
- Standalone deployment: Nginx Dockerfile, K8s Deployment + ConfigMap +
NodePort (30080), runtime config.js for API base URL
- Embedded mode: maven-resources-plugin copies ui/dist into JAR static
resources, SPA forward controller for client-side routing
- CI/CD: UI build step, Docker build/push for server-ui image, K8s
deploy step for UI, UI credential secrets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>