feat(kernel): JWT private-key M2M auth on use_kernel=True - #921
Conversation
Route JWT private-key client-assertion auth (RFC 7523) through the kernel backend. When the caller passes `oauth_jwt_key_file` (+ `oauth_client_id` and `oauth_jwt_kid`, optional `oauth_jwt_passphrase` / `oauth_jwt_algorithm` / `oauth_scopes` / `token_url`), the bridge forwards them to the kernel's `auth_type="oauth-m2m-jwt"`, which signs a short-lived assertion with the private key instead of sending a client secret and owns the token lifecycle. - auth_bridge.py: new JWT branch (checked before shared-secret M2M and PAT, since a private-key file is unambiguous JWT M2M intent); mutually exclusive with oauth_client_secret / credentials_provider; requires client_id + kid. - session.py: forward the new oauth_jwt_* / token_url kwargs into the kernel auth options. - tests: 9 unit tests covering routing, precedence, validation, and ambiguity guards. Verified end-to-end: `SELECT 1` via use_kernel=True against an Azure Databricks warehouse, authenticated by Entra ID against the service principal's registered public certificate. Signed-off-by: Rahul Singhal <rahul.singhal@databricks.com>
There was a problem hiding this comment.
Verdict: 1 Medium · 1 Low
Solid, well-tested addition — routing, precedence, required-field validation, and two of three ambiguity guards are covered. One medium gap: the new JWT M2M path lacks the U2M-collision guard that the shared-secret M2M path has, so oauth_jwt_key_file + auth_type="databricks-oauth" silently resolves to JWT M2M instead of failing loudly. Plus a cosmetic stale-comment numbering nit.
The JWT branch introduced an earlier untyped `kwargs =`, so mypy flagged the M2M branch's `kwargs: Dict[str, Any]` as a redefinition. Move the annotation to the first (JWT) assignment. Signed-off-by: Rahul Singhal <rahul.singhal@databricks.com>
There was a problem hiding this comment.
Verdict: 1 Medium
Solid, well-tested addition of JWT private-key M2M auth to the kernel path — routing, precedence, required-field validation, and two ambiguity guards all have unit coverage. One medium consistency gap: the JWT path lacks the U2M-conflict ambiguity guard its shared-secret sibling has, so oauth_jwt_key_file + auth_type="databricks-oauth" silently routes to M2M against the wrong principal.
…ments Address peco-review-bot review on #921: - Medium: add the missing ambiguity guard for oauth_jwt_key_file + auth_type="databricks-oauth" (U2M intent), mirroring the existing shared-secret M2M + U2M guard. Fails loudly rather than silently resolving to one flow. Covered by a new unit test. - Low: renumber the inline resolution-order comments (PAT→3, U2M→4, creds→5, else→6) to match the docstring after the JWT branch insert. Signed-off-by: Rahul Singhal <rahul.singhal@databricks.com>
|
Can you update https://github.com/databricks/databricks-sql-python/blob/main/CONNECTION_PARAMETERS.md as well? We seem do not have a column for private key JWT |
Address Eric's review on #921: add rows for oauth_jwt_key_file / oauth_jwt_kid / oauth_jwt_passphrase / oauth_jwt_algorithm / token_url to the connection-parameter reference (all kernel-only). Signed-off-by: Rahul Singhal <rahul.singhal@databricks.com>
|
Done — added rows for |
There was a problem hiding this comment.
Verdict: 1 Medium
Solid, well-tested addition — the JWT M2M routing, precedence, required-field validation, and ambiguity guards are coherent and match the new unit tests. One medium doc/code mismatch: token_url is documented as applying to shared-secret M2M but is only forwarded on the JWT branch. Nit: the module docstring at the top of auth_bridge.py still says "Three auth shapes are supported" and omits JWT M2M — worth updating to four.
…ner) Security Scan flagged cryptography@49.0.0 CVE-2026-69247 (GHSA-g6cj-pr64-35w5 / PYSEC-2026-3552, CVSS 8.2) — a Bleichenbacher-style oracle in PKCS#7 EnvelopedData / S-MIME decryption. `cryptography` is a transitive dep (PyJWT[crypto] / oauthlib) used only for OAuth token signing/verification; the connector never decrypts PKCS#7 / S-MIME, so the vulnerable path is never invoked. Pre-existing on main (transitive, not introduced by this PR). Suppress both OSV ids with justification; a clean bump awaits PyJWT/oauthlib floors pulling cryptography>=50. Signed-off-by: Rahul Singhal <rahul.singhal@databricks.com>
Address peco-review-bot: the CONNECTION_PARAMETERS.md row said token_url "applies to shared-secret M2M too", but the bridge only forwarded it on the JWT branch — so an oauth_client_id + oauth_client_secret + token_url connection silently dropped token_url. token_url is an auth-method-agnostic token-endpoint override: JDBC's OAuth2ConnAuthTokenEndpoint is consumed by the client-secret M2M provider, the JWT provider, and the refresh provider alike; the Node driver also forwards it on both branches; and the kernel's oauth-m2m auth_type accepts it (pyo3). Forward it on the shared-secret M2M branch so code matches the doc and the other drivers. Adds a unit test. Signed-off-by: Rahul Singhal <rahul.singhal@databricks.com>
What
Adds OAuth machine-to-machine auth with a JWT private-key client assertion (RFC 7523) on the kernel backend (
use_kernel=True). Instead of a client secret, the kernel signs a short-lived JWT with the service principal's private key and sends it as theclient_assertionin the client-credentials grant; the workspace's OAuth IdP verifies it against the SP's registered public key.Companion to the kernel-side feature (databricks-sql-kernel #249; napi
token_urlin #275) and the parallel databricks-sql-nodejs / databricks-sql-go changes.How
auth_bridge.py— new JWT branch inbuild_kernel_auth_kwargs, checked before shared-secret M2M and PAT (a private-key file is unambiguous JWT M2M intent). Forwardsoauth_client_id+oauth_jwt_key_file+oauth_jwt_kid(+ optionaloauth_jwt_passphrase/oauth_jwt_algorithm/oauth_scopes/token_url) to the kernel'sauth_type="oauth-m2m-jwt". Requires client_id + kid; mutually exclusive withoauth_client_secret/credentials_provider(both raiseNotSupportedError).session.py— forward the newoauth_jwt_*/token_urlconnect kwargs into the kernel auth options.Usage
Testing
tests/unit/test_kernel_auth_bridge.py(routing, precedence over M2M/PAT, required-field validation, ambiguity guards); full file 56 passing.SELECT 1→[Row(n=1)], withconn.session.backendasserted to beKernelDatabricksClient(kernel path, not Thrift) anduse_kerneltrue.Requires
databricks-sql-kernel >= 0.2.0with JWT support.This pull request and its description were written by Isaac.