[ABCA] Implement a Challenge Endpoint - #51574
Conversation
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
There was a problem hiding this comment.
Pull request overview
Implements the ABCA challenge endpoint and challenge validation lifecycle.
Changes:
- Adds challenge endpoint discovery and response support.
- Validates and consumes challenges in PoP JWTs.
- Adds test utilities and integration coverage.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
tests/utils-shared/.../Endpoints.java |
Adds challenge endpoint URL. |
tests/utils-shared/.../ClientAttestationChallengeResponse.java |
Parses challenge responses. |
tests/utils-shared/.../ClientAttestationChallengeRequest.java |
Sends challenge requests. |
tests/utils-shared/.../AbstractOAuthClient.java |
Exposes the request helper. |
tests/base/.../OID4VCBasicWallet.java |
Adds challenges to PoP JWTs. |
tests/base/.../OIDCAttestationBasedClientAuthenticationTest.java |
Tests endpoint and lifecycle behavior. |
services/.../OIDCWellKnownProvider.java |
Advertises challenge endpoint metadata. |
services/.../OIDCLoginProtocolService.java |
Registers the endpoint route. |
services/.../ClientAttestationChallengeEndpoint.java |
Implements challenge issuance. |
services/.../ClientAttestationChallengeResponse.java |
Defines the response model. |
services/.../AttestationBasedClientAuthenticator.java |
Validates and consumes challenges. |
core/.../OIDCConfigurationRepresentation.java |
Adds challenge endpoint metadata. |
core/.../OAuthErrorException.java |
Adds the challenge-required error code. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Signed-off-by: Ogenbertrand <ogenbertrand@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (3)
services/src/main/java/org/keycloak/protocol/oidc/endpoints/ClientAttestationChallengeEndpoint.java:95
- The successful response bypasses
Cors, so a cross-origin wallet receives noAccess-Control-Allow-Originand cannot read either the challenge body or its custom header. Apply CORS to the success response (the SSL error path already does this) and expose the challenge header.
return Response.ok(challengeResponse)
.header(HttpHeaders.CACHE_CONTROL, "no-store")
.header("Pragma", "no-cache")
.header(HttpHeaders.DATE, DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)))
.header(AttestationBasedClientAuthenticator.OAUTH_CLIENT_ATTESTATION_CHALLENGE_HEADER, challenge)
services/src/main/java/org/keycloak/authentication/authenticators/client/AttestationBasedClientAuthenticator.java:146
- Browser clients cannot read this fresh challenge:
TokenEndpoint.java:125exposes onlyAccess-Control-Allow-Methods, andAuthorizeClientUtilapplies that CORS configuration to this authentication failure. AddOAuth-Client-Attestation-Challengeto the token endpoint's exposed headers so the required retry flow works cross-origin.
Response response = Response.fromResponse(ClientAuthUtil.errorResponse(BAD_REQUEST.getStatusCode(), USE_ATTESTATION_CHALLENGE, ex.getMessage()))
.header(OAUTH_CLIENT_ATTESTATION_CHALLENGE_HEADER, ex.getChallenge())
tests/base/src/test/java/org/keycloak/tests/oid4vc/abca/OIDCAttestationBasedClientAuthenticationTest.java:287
- The lifecycle tests cover reuse but not expiration, although rejecting expired challenges and returning
use_attestation_challengewith a fresh header is an explicit requirement. Add an expired-challenge integration case, for example by temporarily setting the nonce lifetime negative as existing OID4VC tests do.
@Test
public void testClientAttestationChallengeCannotBeReused() {
closes: #44769