The app is three things on the target ClickHouse:
user_files/sql.html— the SPA, served at/sqluser_files/sql-config.json— OAuth config, served at/sql/config.json- an
<http_handlers>config fragment wiring those routes
deploy/install.sh does 1 + 2; you wire 3 once.
CLICKHOUSE_PASSWORD=… ./deploy/install.sh \
--ch-host clickhouse.example.com --ch-user admin \
--client-id <oauth-client-id> [--issuer …] [--audience …] [--cluster …] [--secure]For a multi-replica cluster pass --cluster <name>; the installer fans the
bytes to every replica via clusterAllReplicas. The password is read from the
env var or prompted — never placed on the command line.
user_files/is node-local — re-runinstall.shafter a scale-out. ClickHouse does not replicateuser_files/, so a replica added or replaced later starts without the assets. This is a deliberate trade-off (the bootstrap page is then served statically, with no auth). See ASSET-DISTRIBUTION.md for the options (push touser_files, a replicated table +predefined_query_handler, or shipping the asset through config distribution) and their trade-offs.
Add the http_handlers fragment to ClickHouse config.d/ (or push it through
your control plane as config.d/sql-browser.xml) and reload. It adds static
rules for /sql and /sql/config.json and keeps <defaults/> so the dynamic
query handler at / still works. The SPA rule also sends a strict
Content-Security-Policy (default-src 'none', frame-ancestors 'none', and a
connect-src scoped to same-origin + your IdP) plus nosniff and
Referrer-Policy: no-referrer — see README "Security headers".
deploy/http_handlers.xml is the committed default (Google connect-src).
install.sh renders dist/http_handlers.xml with connect-src filled in for
your --issuer; deploy that rendered file. For a manual install with a
non-Google IdP, edit the connect-src line to your issuer + token-endpoint
origins.
The SPA sends Authorization: Bearer <id_token> on every query. ClickHouse must
validate it. Two supported shapes:
- Native token processor (Antalya build with JWT auth): add a
<token_processors>entry pointing at your IdP's JWKS withusername_claim, and a<token>user-directory so users are created on first query. Full step-by-step with generic examples: CLICKHOUSE-OAUTH.md. - Delegated verifier (stock/OSS ClickHouse): run a JWT-verifier service
(e.g. Altinity's ch-jwt-verify)
referenced from
<http_authentication_servers>, define usersIDENTIFIED WITH http SERVER … SCHEME 'BASIC', and set"ch_auth": "basic"in the browserconfig.jsonso the JWT is sent as the Basic password. Full guide with generic examples: CLICKHOUSE-OSS-OAUTH.md.
Either way, the app itself is unchanged — it only sends the bearer.
Register the redirect URI https://<ch-host>/sql with your IdP and put the
issuer + client_id in config.json (the installer does this from
--issuer/--client-id). Whether config.json also needs a client_secret
depends on the IdP and client type — see the "Configuring OAuth" section in the
README for the trade-offs between a PKCE public client, a secret-bearing web
client, and a server-side broker.
Open https://<ch-host>/sql, sign in, and run SELECT currentUser(). It should
return your authenticated identity (not a static user). Schema appears in the
left tree; results stream into the table view.