Skip to content

feat(kernel): JWT private-key M2M auth on use_kernel=True - #921

Merged
rahuls-db merged 6 commits into
mainfrom
feat/kernel-jwt-private-key-m2m
Aug 20, 2026
Merged

feat(kernel): JWT private-key M2M auth on use_kernel=True#921
rahuls-db merged 6 commits into
mainfrom
feat/kernel-jwt-private-key-m2m

Conversation

@rahuls-db

Copy link
Copy Markdown
Collaborator

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 the client_assertion in 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_url in #275) and the parallel databricks-sql-nodejs / databricks-sql-go changes.

How

  • auth_bridge.py — new JWT branch in build_kernel_auth_kwargs, checked before shared-secret M2M and PAT (a private-key file is unambiguous JWT M2M intent). Forwards oauth_client_id + oauth_jwt_key_file + oauth_jwt_kid (+ optional oauth_jwt_passphrase / oauth_jwt_algorithm / oauth_scopes / token_url) to the kernel's auth_type="oauth-m2m-jwt". Requires client_id + kid; mutually exclusive with oauth_client_secret / credentials_provider (both raise NotSupportedError).
  • session.py — forward the new oauth_jwt_* / token_url connect kwargs into the kernel auth options.

Usage

from databricks import sql
conn = sql.connect(
    server_hostname="adb-….azuredatabricks.net",
    http_path="/sql/1.0/warehouses/…",
    use_kernel=True,
    oauth_client_id="<sp-client-id>",
    oauth_jwt_key_file="/path/private_key.pem",
    oauth_jwt_kid="<kid>",
    token_url="https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token",
    oauth_scopes=["<databricks-resource-id>/.default"],
)

Testing

  • 9 new unit tests in tests/unit/test_kernel_auth_bridge.py (routing, precedence over M2M/PAT, required-field validation, ambiguity guards); full file 56 passing.
  • Verified end-to-end against an Azure Databricks warehouse: SELECT 1[Row(n=1)], with conn.session.backend asserted to be KernelDatabricksClient (kernel path, not Thrift) and use_kernel true.

Requires databricks-sql-kernel >= 0.2.0 with JWT support.

This pull request and its description were written by Isaac.

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>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/databricks/sql/backend/kernel/auth_bridge.py
Comment thread src/databricks/sql/backend/kernel/auth_bridge.py
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>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/databricks/sql/backend/kernel/auth_bridge.py
…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>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No issues identified by the review bot.

@eric-wang-1990

Copy link
Copy Markdown
Contributor

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>
@rahuls-db

Copy link
Copy Markdown
Collaborator Author

Done — added rows for oauth_jwt_key_file, oauth_jwt_kid, oauth_jwt_passphrase, oauth_jwt_algorithm, and token_url to CONNECTION_PARAMETERS.md (all kernel-only), grouped with the other OAuth M2M params. (077fb5a7)

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread CONNECTION_PARAMETERS.md
…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>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: 1 Medium

Solid, well-tested change (routing/precedence/validation coverage is thorough). One medium doc/code mismatch: token_url is documented as applying to shared-secret M2M but is only forwarded on the JWT path.

Comment thread CONNECTION_PARAMETERS.md
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>

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No issues identified by the review bot.

@rahuls-db
rahuls-db enabled auto-merge August 20, 2026 19:24
@rahuls-db
rahuls-db added this pull request to the merge queue Aug 20, 2026
Merged via the queue into main with commit b4828fb Aug 20, 2026
50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants