Configure clickhouse-client OAuth login via connection block + CLI flags#1698
Open
BorisTyshkevich wants to merge 3 commits intoantalya-26.1from
Open
Configure clickhouse-client OAuth login via connection block + CLI flags#1698BorisTyshkevich wants to merge 3 commits intoantalya-26.1from
BorisTyshkevich wants to merge 3 commits intoantalya-26.1from
Conversation
Replaces the oauth_client.json credentials file with first-class <oauth-*> fields in <connections_credentials>/<connection> blocks and matching --oauth-* command-line flags. Both sources merge into the same config layer; CLI wins on conflict (standard --user / --port behavior). Connection block / CLI fields: <login>browser|device|""</login> --login=browser|device|"" <oauth-url>...</oauth-url> --oauth-url=... <oauth-client-id>...</oauth-client-id> --oauth-client-id=... <oauth-audience>...</oauth-audience> --oauth-audience=... <oauth-client-secret>...</oauth-client-secret> --oauth-client-secret=... <oauth-callback-port>...</oauth-callback-port> --oauth-callback-port=... Bare <login></login> (or bare --login) keeps the existing ClickHouse Cloud auto-login path. Explicit browser/device modes require oauth-url + oauth-client-id and run via the OAuthLogin id-token path that PR #1606 introduced for --login=device. Endpoint discovery: when <oauth-url> is set the client fetches <issuer>/.well-known/openid-configuration and fills authorization / token / device_authorization endpoints. Previously OIDC discovery only filled the device endpoint; OAuthProviderPolicy is refactored to share the discovery-document fetch and to publish a public populateEndpointsFromOIDCDiscovery() helper. Browser-flow loopback port: <oauth-callback-port> defaults to 0 ("any port"; kernel picks an ephemeral port at flow start). 0 is RFC 8252 §7.3 semantics — only IdPs that honor the §7.3 port-wildcard rule for loopback redirects accept it (currently Google). Auth0 does literal-string match on registered callback URLs and rejects any unregistered port; users with Auth0 must pin a non-zero port and register http://127.0.0.1:<port>/callback. The doc page covers this explicitly. Removed: - loadOAuthCredentials() and oauth_client.json. The Google-format JSON loader is replaced by config/CLI fields plus OIDC discovery; the JSON file is no longer read or referenced anywhere. - --oauth-credentials CLI flag. Same. - gtest_oauth_login.cpp's JSON-loader test cases (kept the PKCE / base64 cases, which test the auth-code flow primitives). Auth-code flow success page is restyled (centered card, light/dark scheme, brand line) so the post-redirect tab is unmistakable instead of `<html><body>Authentication successful.</body></html>`. Docs: - New page docs/en/interfaces/cli-oauth-login.md covering modes, per-provider notes (Auth0, Google, Keycloak/Entra), server-side cross-reference to JWT validation, and the device-flow security considerations (RFC 8628 §5.3 Remote Phishing) explicitly noting the flow's suitability for testing/public services and unsuitability for sensitive DBMS access. - cli.md updated to point at the new doc and the obsolete "OAuth credentials file" section is removed. Stateless test 03749_cloud_endpoint_auth_precedence.sh updated for the new error messages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Boris Tyshkevich <btyshkevich@altinity.com>
parseConnectionsCredentials parsed the field into ConnectionsCredentials::oauth_callback_port, but Client::initialize forgot to mirror it into config() the way it does for the other <oauth-*> fields. The CLI plumbing went through correctly via config().setUInt(...), but the connection-block path silently dropped the value, so the loopback server kept binding port 0 (kernel-picks) and Auth0 rejected the unregistered port. Add the missing configuration.setUInt mirror, parallel to oauth-url / oauth-client-id / oauth-audience / oauth-client-secret. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Boris Tyshkevich <btyshkevich@altinity.com>
Signed-off-by: Boris Tyshkevich <btyshkevich@altinity.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the
oauth_client.jsoncredentials file with first-class<oauth-*>fields in<connections_credentials>/<connection>blocks and matching--oauth-*CLI flags. Both sources merge into the same config layer; CLI wins on conflict (standard--user/--portprecedence).CLI parity:
<login>--login=browser|device|""<oauth-url>--oauth-url<oauth-client-id>--oauth-client-id<oauth-audience>--oauth-audience<oauth-client-secret>--oauth-client-secret<oauth-callback-port>--oauth-callback-portWhat's gone
loadOAuthCredentials()andoauth_client.json. Replaced by config/CLI fields plus full OIDC discovery.--oauth-credentialsCLI flag.gtest_oauth_login.cpp's JSON-loader test cases (kept the PKCE / base64 cases).What's new
<issuer>/.well-known/openid-configuration; nowauthorization_endpoint,token_endpoint, anddevice_authorization_endpointall auto-fill, so<oauth-url>alone is enough for any compliant OIDC IdP.OAuthProviderPolicyis refactored to share the discovery-document fetch and exposespopulateEndpointsFromOIDCDiscovery().<oauth-callback-port>field. Default0= "any port" (RFC 8252 §7.3 ephemeral binding) — works only with §7.3-compliant IdPs (currently Google). Non-zero pins the loopback port for IdPs that do literal-string match on the registered callback URL (Auth0). The doc covers this contract explicitly.<html><body>Authentication successful.</body></html>.Docs
docs/en/interfaces/cli-oauth-login.mdcovering modes, per-provider notes (Auth0 / Google / Keycloak / Entra ID), server-side cross-reference to JWT validation, and the device-flow security considerations (RFC 8628 §5.3 Remote Phishing) explicitly noting the flow's suitability for testing/public services and unsuitability for sensitive DBMS access.cli.mdupdated to point at the new doc; obsolete "OAuth credentials file" section removed.Migration
Users of
--login=device --oauth-credentials path/to/oauth_client.json:becomes
Refresh-token cache (
~/.clickhouse-client/oauth_cache.json) format is unchanged; existing cached tokens keep working across the migration.Dependencies
Client::loginsegfault fix (Fix Client::login segfault on empty hosts_and_ports (#103603) #1696). Without it, this feature path crashes on--connectioninvocations before reaching OAuth.Test plan
antalya-26.1tests/queries/0_stateless/03749_cloud_endpoint_auth_precedence.sh— assertions updated for the new error messages, tests 9/10/11 pass locallyclickhouse client --connection antalya -q "SELECT 1"against Auth0 with<login>browser</login>+<oauth-callback-port>49152</oauth-callback-port>— auth code flow completes, browser shows the new success page, query returns<login>device</login>against Auth0 — device code printed, post-approval token obtained, query returns--oauth-callback-port=49153overrides<oauth-callback-port>49152</oauth-callback-port>)<login>browser</login>and default<oauth-callback-port>(=0); expect kernel-picks port to work since Google honors RFC 8252 §7.3oauth_client.json(grep is clean: 0 hits in src/ after this PR)🤖 Generated with Claude Code