fix(xtest): detect java dpop support via supports subcommand - #558
Conversation
The java-sdk removed the user-facing --dpop/--dpop-key CLI flags (opentdf/java-sdk#374); DPoP is now always-on and capability is exposed through the `supports` subcommand. The old detector grepped `help encrypt` for --dpop, which now finds nothing and marks java as not supporting dpop. Delegate to `cmdline.jar supports dpop` to match the existing dpop_nonce_challenge case. Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
|
There was a problem hiding this comment.
Code Review
This pull request simplifies the 'dpop' support check in the xtest/sdk/java/cli.sh script. It replaces a pipeline that grepped the help output of the encrypt command with a direct call to supports dpop on the Java command-line utility. There are no review comments, and I have no additional feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe Java CLI script’s ChangesDPoP feature probing
Estimated code review effort: 1 (Trivial) | ~3 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
## Summary Adds comprehensive DPoP (RFC 9449) support to the Java SDK as part of the Keycloak v26 upgrade and DPoP implementation effort. **Related Jira:** https://virtru.atlassian.net/browse/DSPX-3397 **Test Scenario:** xtest/scenarios/DSPX-3397.yaml (in tests repo) **Green e2e Test Run:** https://github.com/opentdf/tests/actions/runs/29624686479 ## Changes ### Core DPoP proof generation - **TokenSource**: Generates RFC 9449-compliant DPoP proofs via Nimbus `DefaultDPoPProofFactory`, with claims `jti`, `htm`, `htu`, `iat` (plus `ath` for resource endpoints). - `htu` claim strips query and fragment per RFC 9449 §4.2. - Token scheme (`DPoP` vs `Bearer`) modeled as an enum; token + scheme read atomically to avoid a refresh-time data race. ### Server-issued nonce handling (full retry — implemented) - **Token endpoint (§8.2)**: On `use_dpop_nonce`, retries the token request once with the AS-provided nonce. - **Resource server (§9)**: okhttp-level interceptor retries once with the `DPoP-Nonce` from a `WWW-Authenticate` challenge; drops the stale proof header on the retry. - Per-origin nonce cache (keyed by scheme/host/port, with default-port normalization); nonces cached from responses regardless of status. ### Key configuration & validation - **SDKBuilder**: `dpopKey(...)` accepts a caller-supplied RSA **or EC** key; auto-generates an ephemeral key if none provided. An EC DPoP key triggers a separate RSA-2048 SRT key. - **DpopKeyValidation** (new): central validation — rejects public-only JWKs, enforces key-type/algorithm compatibility, infers the EC algorithm from the curve. - Connect-GET disabled on the authenticated client to protect the `htm` claim. ### Bearer fallback - Falls back to `Bearer` scheme when the AS returns a non-DPoP-bound token, with a warning; stale DPoP proof is stripped so a Bearer request never carries a DPoP header. - Fails loudly when DPoP is requested but the well-known omits `platform_issuer`. ### CLI (cmdline) DPoP is **always-on** for any authenticated client, so the CLI needs no DPoP flags: - The SDK auto-generates a DPoP key when none is configured, so `encrypt`, `decrypt`, and metadata commands are sender-constrained without any opt-in. - No `--dpop` / `--dpop-key` flags are exposed (an earlier revision added them; they were removed together with `CliDpopOptions` since they served no purpose once DPoP is unconditional). This mirrors the go-sdk direction in opentdf/platform#3581, which likewise dropped otdfctl's `--dpop` parameters. - **Feature detection via the `supports` subcommand** (no help-text scraping required): - `tdf supports <feature>` exits `0` if supported, `1` otherwise. - `tdf supports` with no argument lists all supported feature names, one per line. - `--json` emits a JSON object mapping feature name → boolean, e.g. `{"dpop":true}` or, with no argument, `{"dpop":true,"dpop_nonce_challenge":true}`. ## Feature Detection ```bash tdf supports dpop # exits 0 if supported tdf supports dpop_nonce_challenge # exits 0 if supported tdf supports # lists supported features, one per line tdf supports --json # {"dpop":true,"dpop_nonce_challenge":true} tdf supports dpop --json # {"dpop":true} ``` ## Testing New coverage: `TokenSourceTest`, `DPoPRetryInterceptorTest`, `AuthInterceptorConnectPathTest` (Kotlin), and `CommandTest` — covering nonce caching/origin isolation, both retry paths, EC/RSA key handling, and the `supports` subcommand (bare exit codes, no-arg listing, and `--json` output). ## Related PRs - **tests repo**: opentdf/tests#558 — updates the xtest Java `cli.sh` to detect `dpop` via `supports dpop` instead of grepping the removed `--dpop` flag. - **platform repo**: DSPX-3397-platform-service, DSPX-3397-platform-go-sdk (opentdf/platform#3581) - **web-sdk repo**: DSPX-3397-web-sdk ## Checklist - [x] DPoP proof generation (htm/htu/iat/jti/ath) - [x] Per-origin nonce caching - [x] Full 401/nonce retry — token endpoint (§8.2) and resource server (§9) - [x] RSA and EC DPoP keys; caller-supplied or ephemeral - [x] Central DPoP key validation - [x] Bearer fallback for non-DPoP-bound tokens - [x] Always-on DPoP; no CLI flags needed (removed `--dpop`/`--dpop-key` + `CliDpopOptions`) - [x] `supports` feature detection: bare exit code, no-arg listing, `--json` - [ ] xtest integration tests (pending tests repo coordination — opentdf/tests#558) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a `supports` CLI subcommand (plain list or `--json` feature support results with canonical casing). * Added `-v/--verbose` for more detailed error output. * Improved DPoP handling: configurable DPoP keys/algorithms, conditional DPoP headers, nonce caching, and single-retry behavior for `use_dpop_nonce` (with correct Bearer downgrade). * Added stricter validation and clearer errors for missing CLI credentials and incompatible DPoP configuration. * **Documentation** * Updated build instructions for setting `JAVA_HOME` via Homebrew. * **Testing / Build** * Added/expanded CLI and SDK test coverage and explicit test dependencies for Java/Kotlin. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>



Problem
The java-sdk removed the user-facing
--dpop/--dpop-keyCLI flags in opentdf/java-sdk#374. DPoP is now always-on by default, and capability is exposed through thesupportssubcommand instead.The Java wrapper's
supports dpopcase still detects DPoP by greppinghelp encryptfor the--dpopflag:With the flags gone this grep finds nothing, exits 1, and xtest skips java DPoP tests:
The sibling
dpop_nonce_challengecase already delegates to thesupportssubcommand and is unaffected.Fix
Detect
dpopthe same waydpop_nonce_challengealready does — via thesupportssubcommand, whose exit code is the capability contract (0 = supported, 1 = not):set -o pipefailis dropped since there's no longer a pipe.Verification
With a current java
cmdline.jarinstalled:./cli.sh supports dpop; echo $?prints0, anddpop_nonce_challengestill returns0. Onlyxtest/sdk/java/cli.shchanges; other SDK wrappers are untouched.Summary by CodeRabbit