Skip to content

feat: add OAuth Authorization Code + PKCE authentication mode#188

Draft
mkaufmann wants to merge 8 commits into
mainfrom
feat/oauth-pkce-auth-code
Draft

feat: add OAuth Authorization Code + PKCE authentication mode#188
mkaufmann wants to merge 8 commits into
mainfrom
feat/oauth-pkce-auth-code

Conversation

@mkaufmann
Copy link
Copy Markdown
Member

@mkaufmann mkaufmann commented May 15, 2026

Summary

  • Adds an interactive OAuth Authorization Code flow with PKCE (RFC 7636) as a fourth authentication mode (authMode=AUTH_CODE_PKCE). The driver opens the user's browser to /services/oauth2/authorize, listens on a loopback HTTP port for the redirect, and exchanges the resulting code for an access token using a per-login code_verifier. PKCE doesn't apply to the existing password / JWT-bearer / refresh_token grants — those don't take a code_verifier — so this is a net-new flow rather than a tweak to an existing one.
  • Removes a customer pain point: refreshToken mode previously required completing the OAuth dance externally and pasting the resulting refresh token into a Properties. With AUTH_CODE_PKCE, the driver integrates this flow

What changed (observable)

  • New auth mode AUTH_CODE_PKCE, opt-in via authMode=AUTH_CODE_PKCE.
  • New connection properties: oauthScope (default cdp_query_api api refresh_token), redirectPort (default 0 = ephemeral; pin to a specific port to match the ECA's registered Callback URL — wildcard ports are not supported), browserAuthTimeoutSeconds (default 120).
  • clientSecret is optional for this mode (public-client ECAs don't have one); it's still passed through if set, for confidential ECAs.
  • README's "Authentication" section gained the fourth flow, an ECA setup table (Enable OAuth, Callback URL, scopes, Require PKCE, public-client toggles), and a "Headless / scripted ECA creation" section pointing to the flxbl-io ECA guide with minimal SFDX-source XML for ExternalClientApplication, ExtlClntAppOauthSettings, and ExtlClntAppGlobalOauthSettings.

Internals

  • PkceCodes — RFC 7636 verifier+S256 challenge generator (verified against Appendix B test vector).
  • LoopbackCallbackServer — single-shot 127.0.0.1-bound com.sun.net.httpserver HTTP server. Captures code/state, surfaces OAuth error params, returns a "you can close this tab" page. Closes idempotently.
  • BrowserLauncher interface — java.awt.Desktop.browse() with stderr fallback for headless environments. Tests inject a fake; production code uses the default.
  • DataCloudTokenProvider.runAuthorizationCodeDance() — orchestrates verifier+challenge+state generation, binds the loopback server, opens the browser, awaits the callback, verifies state (CSRF defense), and stashes the verifier into a one-shot PendingAuthCodeExchange that buildAuthenticate() reads on the very next call.
  • The token-exchange leg (/services/a360/token) is unchanged — only the OAuth-token leg differs.

Out of scope (deliberately)

  • Default-flipping for existing modes — Flipping a default is a doc/announcement decision that's separate from making the flow available. This PR makes it available; flipping it can be a follow-up once we've gathered field signal.
  • Token persistence (caching the refresh token to disk so the browser dance doesn't repeat each launch) — useful follow-up, but pulls in storage/permissions decisions.
  • docs/oauth-flow.md — that's a local-only doc not on main. The README is the source of truth shipped to customers.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

❌ Patch coverage is 75.95628% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.15%. Comparing base (f47714f) to head (c60ecaa).

Files with missing lines Patch % Lines
...ce/datacloud/jdbc/auth/LoopbackCallbackServer.java 77.46% 10 Missing and 6 partials ⚠️
...alesforce/datacloud/jdbc/auth/BrowserLauncher.java 21.42% 11 Missing ⚠️
...ce/datacloud/jdbc/auth/DataCloudTokenProvider.java 87.50% 6 Missing and 2 partials ⚠️
.../datacloud/jdbc/auth/SalesforceAuthProperties.java 69.56% 3 Missing and 4 partials ⚠️
.../com/salesforce/datacloud/jdbc/auth/PkceCodes.java 81.81% 2 Missing ⚠️

❌ Your patch check has failed because the patch coverage (75.95%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff              @@
##               main     #188      +/-   ##
============================================
- Coverage     82.37%   82.15%   -0.22%     
- Complexity     1871     1900      +29     
============================================
  Files           125      128       +3     
  Lines          5020     5205     +185     
  Branches        540      562      +22     
============================================
+ Hits           4135     4276     +141     
- Misses          642      674      +32     
- Partials        243      255      +12     
Components Coverage Δ
JDBC Core 83.14% <ø> (ø)
JDBC Main 40.69% <ø> (ø)
JDBC HTTP 86.28% <75.95%> (-4.02%) ⬇️
JDBC Utilities 65.25% <ø> (ø)
Spark Datasource ∅ <ø> (∅)
Files with missing lines Coverage Δ
.../com/salesforce/datacloud/jdbc/auth/PkceCodes.java 81.81% <81.81%> (ø)
.../datacloud/jdbc/auth/SalesforceAuthProperties.java 88.77% <69.56%> (-5.82%) ⬇️
...ce/datacloud/jdbc/auth/DataCloudTokenProvider.java 86.79% <87.50%> (+0.39%) ⬆️
...alesforce/datacloud/jdbc/auth/BrowserLauncher.java 21.42% <21.42%> (ø)
...ce/datacloud/jdbc/auth/LoopbackCallbackServer.java 77.46% <77.46%> (ø)

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

1 participant