8000 Resolve ABCA attester key from x5c JOSE header by adityaanikam · Pull Request #52883 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

Resolve ABCA attester key from x5c JOSE header - #52883

Merged
mposolda merged 1 commit into
keycloak:mainfrom
adityaanikam:abca-x5c-attester-key
Nov 5, 2026
Merged

mposolda merged 1 commit into
keycloak:mainfrom
adityaanikam:abca-x5c-attester-key

Conversation

@adityaanikam
@adityaanikam adityaanikam commented Sep 17, 2026 •
Copy link
Copy Markdown
Contributor

What

AttestationBasedClientAuthenticator.findAttesterKey only resolved the attester's signing key through the kid JOSE header via JWKS. A Client Attestation JWT that carries its signing certificate chain in the x5c header (no kid) was rejected before any verification, with Invalid attester kid: null; x5c was never read.

Why

draft-ietf-oauth-attestation-based-client-auth-10 Section 9.8 lists x5c as a valid attester-key resolution path, and the WE BUILD interoperability profile's reference wallet sends x5c with no kid. #51042 already added X.509 chain validation (TrustMaterialResolver.validateX509Chain) for key attestations at the credential endpoint; this reuses the same mechanism for client attestations.

How

In findAttesterKey, when the JOSE header carries a non-empty x5c, validate it against the X.509 trust anchors of the configured attester_trust_idps and use the leaf certificate's public key. Otherwise, fall back to the existing kid/JWKS resolution unchanged.

Testing

Extended OIDCAttestationBasedClientAuthenticationTest with two new tests. testClientAttestationHappyFlowWithX5c sends an attestation JWT signed with a leaf cert chaining to a default-trust identity provider configured in X.509 mode, with no kid, and the token request succeeds. testClientAttestationX5cUntrustedChainIsRejected sends the same, but the leaf cert chains to a CA that is not configured as trusted, and the token request is rejected.

Closes #52766

@adityaanikam
adityaanikam requested a review from a team as a code owner September 17, 2026 09:08
Copilot AI balanced review requested due to automatic review settings September 17, 2026 09:08
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The implementation satisfies the requested behavior; the identified unused helper is non-blocking cleanup.

Pull request overview

Adds trusted x5c certificate-chain resolution for ABCA attester keys while retaining JWKS fallback.

Changes:

  • Validates x5c chains through configured trust-material providers.
  • Adds trusted and untrusted certificate-chain integration tests.
File summaries
File Description
AttestationBasedClientAuthenticator.java Resolves attester keys from x5c before falling back to kid.
OIDCAttestationBasedClientAuthenticationTest.java Tests trusted and untrusted x5c authentication.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced (auto)

Note

Copilot is running an experiment and ran this review at Balanced.


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

8000
@mposolda mposolda self-assigned this Sep 17, 2026
@mposolda
Copy link
Copy Markdown
Contributor

@Awambeng @Ogenbertrand @IngridPuppet @forkimenjeckayang Do you have opportunity to review the PR? Also tagging, so that you are aware of this PR as not sure if it is in the conflict (or relation) with some of your work?

@Awambeng Awambeng left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the solid fix, @adityaanikam! However, I left a few minor comments. Could you please check them when you get a chance?

Copilot AI review requested due to automatic review settings September 18, 2026 18:45
@adityaanikam
adityaanikam force-pushed the abca-x5c-attester-key branch 2 times, most recently from 1991b7a to e754e99 Compare September 18, 2026 18:47
@adityaanikam
Copy link
Copy Markdown
Contributor Author

Thanks for the review, all addressed in the updated commit (rebased on main, still one commit).

Formatting fixed (that block was tab indented by mistake) and the redundant import removed. The call site now matches the suggestion. The error message for the null case now says no X.509 trust anchors are configured. I removed the unused assertX5cAttestationOutcome helper Copilot flagged, since runX5cAttestationTest already covers that flow.

On reusing OID4VCProofTestUtils: it only had createRsaKeyPair and createEcKeyPair, nothing that builds a CA plus end entity chain, so there was nothing to reuse directly. I moved the certificate generation there as createCaCertificate and createEndEntityCertificate, so this test no longer carries its own BouncyCastle code and other tests can use them. Happy to inline it back or rename if you'd prefer a different shape.

Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

@Awambeng
Copy link
Copy Markdown
Contributor

Hi @adityaanikam, could you please review the Copilot comments and address the ones that make sense?

Copilot AI review requested due to automatic review settings September 22, 2026 21:59
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings September 22, 2026 22:02
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Awambeng Awambeng left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for considering my previous comments, @adityaanikam. I left a few minor ones for you to check.

Could you also review the Copilot comments, resolve them or leave feedback if they aren't valid, and mark each conversation as resolved once addressed?

Copilot AI review requested due to automatic review settings September 26, 2026 18:59
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings September 26, 2026 19:02
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@adityaanikam
Copy link
Copy Markdown
Contributor Author

Thanks @Awambeng, all addressed in the updated commit (still one commit).

Test with no kid: the attester key now has its kid cleared, so the x5c tests send a JWS with no kid in the header and cover the case from #52766.

Fallback to kid: good catch. validateX509Chain returns null when no X.509 trust anchors are configured (JWKS-only) and throws when a configured chain fails validation, so a client sending both kid and x5c would have regressed from working via kid before this change. findAttesterKey now falls back to the kid lookup on null. If there is no kid either, the "No X.509 trust anchors are configured" message is kept. I added testClientAttestationWithKidAndX5cFallsBackToKidWithoutX509TrustAnchors for this and moved the shared token request part of the x5c tests into a helper so the tests do not duplicate it.

Full chain in x5c: I left this one as is. The leaf is signed directly by the configured trust anchor and real chains often omit the anchor, so adding it would not be closer to a real request. Covering an intermediate would need extra certificate scaffolding that seems out of proportion here.

Copilot comments: findAttesterKey now creates one TrustMaterialResolver, signCertificate is documented as RSA-only, the rejection assertion checks the invalid_client_attestation error and that no access token is returned, and certificate serial numbers reuse one SecureRandom.

@mposolda mposolda left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@adityaanikam @Awambeng Thanks for the PR and review! Approving based on the @Awambeng review. I hope to merge if tests are OK (there was a failure in MSSQL, which looks unrelated to this change)

@dominikschlosser
Copy link
Copy Markdown
Contributor

This overlaps with #54522 (ETSI LoTE trust), which needs this change for client attestations. I'll drop my version from #54522 and build on this PR instead.

One gap I found while testing: when a client trusts a provider with X.509 trust anchors, an attestation without x5c still falls back to kid. If another configured provider has a JWKS with that kid, the X.509 policy is bypassed. It would be safer to call validateX509Chain for every attestation, also without x5c. It throws when X.509 trust is configured and no chain is sent, and returns null when no provider uses X.509 trust, so the kid fallback still works for JWKS-only setups. Happy to send that as a follow-up if you'd rather keep this PR as is.

AttestationBasedClientAuthenticator.findAttesterKey required a kid
and looked it up via JWKS, rejecting any Client Attestation JWT that
carries its signing certificate chain in the x5c header instead, with
"Invalid attester kid: null". x5c was never read.

Prefer x5c when present: validate the chain against the X.509 trust
anchors of the configured attester_trust_idps using
TrustMaterialResolver.validateX509Chain, the mechanism keycloak#51042 added
for key attestations, and use the leaf certificate's public key. Fall
back to the existing kid/JWKS path when x5c is absent.

Closes keycloak#52766

Signed-off-by: adityaanikam <adityanikam9502@gmail.com>
@mposolda
mposolda force-pushed the abca-x5c-attester-key branch from 3c328f5 to b04d334 Compare November 5, 2026 11:17
Copilot AI balanced review requested due to automatic review settings November 5, 2026 11:17
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@mposolda mposolda left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@adityaanikam @Awambeng @dominikschlosser Thanks for the PR and reviews!

@dominikschlosser Created #54683 as a follow-up to your comment

@mposolda
mposolda merged commit f2d2da5 into keycloak:main Nov 5, 2026
104 of 106 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

5 participants

0