From 4a775a87483ad7e638f0be8a9bea4cae283b8961 Mon Sep 17 00:00:00 2001 From: forkimenjeckayang Date: Fri, 24 Apr 2026 12:28:31 +0000 Subject: [PATCH 1/2] refactor(test-oid4vci): add built-in encrypted credential request/response support in OID4VC test client Signed-off-by: forkimenjeckayang --- .../OID4VCIssuerEndpointEncryptionTest.java | 178 +++++------------- .../oauth/oid4vc/Oid4vcCredentialRequest.java | 62 +++++- .../oid4vc/Oid4vcCredentialResponse.java | 28 ++- 3 files changed, 139 insertions(+), 129 deletions(-) diff --git a/tests/base/src/test/java/org/keycloak/tests/oid4vc/OID4VCIssuerEndpointEncryptionTest.java b/tests/base/src/test/java/org/keycloak/tests/oid4vc/OID4VCIssuerEndpointEncryptionTest.java index 5d3875d8fae6..a59a37d4c53b 100644 --- a/tests/base/src/test/java/org/keycloak/tests/oid4vc/OID4VCIssuerEndpointEncryptionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/oid4vc/OID4VCIssuerEndpointEncryptionTest.java @@ -1,21 +1,15 @@ package org.keycloak.tests.oid4vc; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; -import java.security.PublicKey; import java.security.interfaces.RSAPublicKey; import java.util.List; import java.util.Map; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.client.Client; -import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.client.Invocation; import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.HttpHeaders; @@ -26,9 +20,6 @@ import org.keycloak.TokenVerifier; import org.keycloak.admin.client.Keycloak; import org.keycloak.common.util.Base64Url; -import org.keycloak.jose.jwe.JWE; -import org.keycloak.jose.jwe.JWEException; -import org.keycloak.jose.jwe.JWEHeader; import org.keycloak.jose.jwk.JWK; import org.keycloak.jose.jwk.JWKParser; import org.keycloak.jose.jwk.RSAPublicJWK; @@ -64,7 +55,6 @@ import static org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerWellKnownProvider.ATTR_REQUEST_ENCRYPTION_REQUIRED; import static org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerWellKnownProvider.ATTR_RESPONSE_ENCRYPTION_REQUIRED; import static org.keycloak.tests.oid4vc.OID4VCProofTestUtils.generateJwtProof; -import static org.keycloak.utils.MediaType.APPLICATION_JWT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -95,25 +85,21 @@ void testRequestCredentialWithEncryption() throws Exception { .setProofs(new Proofs().setJwt(List.of(generateJwtProof(flow.issuer(), flow.cNonce())))) .setCredentialResponseEncryption(new CredentialResponseEncryption().setEnc(A256GCM).setJwk(responseJwk)); - String requestJson = JsonSerialization.writeValueAsString(credentialRequest); JWK requestEncryptionJwk = flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0]; - String encryptedRequest = encryptRequest(requestJson, requestEncryptionJwk, false); - - try (Client httpClient = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) { - WebTarget target = httpClient.target(flow.issuerMetadata().getCredentialEndpoint()); - try (Response response = target.request() - .header(HttpHeaders.AUTHORIZATION, "Bearer " + flow.token()) - .post(Entity.entity(encryptedRequest, APPLICATION_JWT))) { - assertEquals(200, response.getStatus()); - assertEquals(APPLICATION_JWT, response.getMediaType().toString()); - - CredentialResponse decryptedResponse = decryptJweResponse(response.readEntity(String.class), responsePrivateKey); - assertNotNull(decryptedResponse.getCredentials()); - JsonWebToken jwt = TokenVerifier.create((String) decryptedResponse.getCredentials().get(0).getCredential(), JsonWebToken.class).getToken(); - VerifiableCredential credential = JsonSerialization.mapper.convertValue(jwt.getOtherClaims().get("vc"), VerifiableCredential.class); - assertTrue(credential.getCredentialSubject().getClaims().containsKey("scope-name")); - } - } + var response = oauth.oid4vc() + .credentialRequest(credentialRequest) + .bearerToken(flow.token()) + .encryptRequest(requestEncryptionJwk, false) + .send(); + + assertEquals(200, response.getStatusCode()); + assertEquals("application/jwt", response.getHeader(HttpHeaders.CONTENT_TYPE)); + + CredentialResponse decryptedResponse = response.getCredentialResponse(responsePrivateKey); + assertNotNull(decryptedResponse.getCredentials()); + JsonWebToken jwt = TokenVerifier.create((String) decryptedResponse.getCredentials().get(0).getCredential(), JsonWebToken.class).getToken(); + VerifiableCredential credential = JsonSerialization.mapper.convertValue(jwt.getOtherClaims().get("vc"), VerifiableCredential.class); + assertTrue(credential.getCredentialSubject().getClaims().containsKey("scope-name")); } @Test @@ -140,21 +126,16 @@ void testEncryptedCredentialRequestWithCompression() throws Exception { .setProofs(new Proofs().setJwt(List.of(generateJwtProof(flow.issuer(), flow.cNonce())))) .setCredentialResponseEncryption(new CredentialResponseEncryption().setEnc(A256GCM).setJwk(responseJwk)); - String requestJson = JsonSerialization.writeValueAsString(credentialRequest); JWK requestEncryptionJwk = flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0]; - String encryptedRequest = encryptRequest(requestJson, requestEncryptionJwk, true); - - try (Client httpClient = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) { - WebTarget target = httpClient.target(flow.issuerMetadata().getCredentialEndpoint()); - try (Response response = target.request() - .header(HttpHeaders.AUTHORIZATION, "Bearer " + flow.token()) - .post(Entity.entity(encryptedRequest, APPLICATION_JWT))) { - assertEquals(200, response.getStatus()); - assertEquals(APPLICATION_JWT, response.getMediaType().toString()); - CredentialResponse decryptedResponse = decryptJweResponse(response.readEntity(String.class), responsePrivateKey); - assertNotNull(decryptedResponse.getCredentials()); - } - } + var response = oauth.oid4vc() + .credentialRequest(credentialRequest) + .bearerToken(flow.token()) + .encryptRequest(requestEncryptionJwk, true) + .send(); + assertEquals(200, response.getStatusCode()); + assertEquals("application/jwt", response.getHeader(HttpHeaders.CONTENT_TYPE)); + CredentialResponse decryptedResponse = response.getCredentialResponse(responsePrivateKey); + assertNotNull(decryptedResponse.getCredentials()); } finally { setRealmAttributes(Map.of( ATTR_REQUEST_ENCRYPTION_REQUIRED, "false", @@ -199,19 +180,13 @@ void testRequestCredentialWithUnsupportedResponseEncryption() throws Exception { .setProofs(new Proofs().setJwt(List.of(generateJwtProof(flow.issuer(), flow.cNonce())))) .setCredentialResponseEncryption(new CredentialResponseEncryption().setEnc("A128GCM").setJwk(responseJwk)); - String requestJson = JsonSerialization.writeValueAsString(request); - String encryptedRequest = encryptRequest(requestJson, flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0], false); - - try (Client httpClient = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) { - WebTarget target = httpClient.target(flow.issuerMetadata().getCredentialEndpoint()); - try (Response response = target.request() - .header(HttpHeaders.AUTHORIZATION, "Bearer " + flow.token()) - .post(Entity.entity(encryptedRequest, APPLICATION_JWT))) { - assertEquals(400, response.getStatus()); - ErrorResponse error = JsonSerialization.readValue(response.readEntity(String.class), ErrorResponse.class); - assertEquals(ErrorType.INVALID_ENCRYPTION_PARAMETERS.getValue(), error.getError()); - } - } + var response = oauth.oid4vc() + .credentialRequest(request) + .bearerToken(flow.token()) + .encryptRequest(flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0], false) + .send(); + assertEquals(400, response.getStatusCode()); + assertEquals(ErrorType.INVALID_ENCRYPTION_PARAMETERS.getValue(), response.getError()); } @Test @@ -225,19 +200,13 @@ void testRequestCredentialWithUnsupportedResponseCompression() throws Exception .setProofs(new Proofs().setJwt(List.of(generateJwtProof(flow.issuer(), flow.cNonce())))) .setCredentialResponseEncryption(new CredentialResponseEncryption().setEnc(A256GCM).setZip("UNSUPPORTED-ZIP").setJwk(responseJwk)); - String requestJson = JsonSerialization.writeValueAsString(request); - String encryptedRequest = encryptRequest(requestJson, flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0], false); - - try (Client httpClient = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) { - WebTarget target = httpClient.target(flow.issuerMetadata().getCredentialEndpoint()); - try (Response response = target.request() - .header(HttpHeaders.AUTHORIZATION, "Bearer " + flow.token()) - .post(Entity.entity(encryptedRequest, APPLICATION_JWT))) { - assertEquals(400, response.getStatus()); - ErrorResponse error = JsonSerialization.readValue(response.readEntity(String.class), ErrorResponse.class); - assertEquals(ErrorType.INVALID_ENCRYPTION_PARAMETERS.getValue(), error.getError()); - } - } + var response = oauth.oid4vc() + .credentialRequest(request) + .bearerToken(flow.token()) + .encryptRequest(flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0], false) + .send(); + assertEquals(400, response.getStatusCode()); + assertEquals(ErrorType.INVALID_ENCRYPTION_PARAMETERS.getValue(), response.getError()); } @Test @@ -250,19 +219,13 @@ void testRequestCredentialWithInvalidJWK() throws Exception { .setProofs(new Proofs().setJwt(List.of(generateJwtProof(flow.issuer(), flow.cNonce())))) .setCredentialResponseEncryption(new CredentialResponseEncryption().setEnc(A256GCM).setJwk(invalidJwk)); - String requestJson = JsonSerialization.writeValueAsString(request); - String encryptedRequest = encryptRequest(requestJson, flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0], false); - - try (Client httpClient = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) { - WebTarget target = httpClient.target(flow.issuerMetadata().getCredentialEndpoint()); - try (Response response = target.request() - .header(HttpHeaders.AUTHORIZATION, "Bearer " + flow.token()) - .post(Entity.entity(encryptedRequest, APPLICATION_JWT))) { - assertEquals(400, response.getStatus()); - ErrorResponse error = JsonSerialization.readValue(response.readEntity(String.class), ErrorResponse.class); - assertEquals(ErrorType.INVALID_ENCRYPTION_PARAMETERS.getValue(), error.getError()); - } - } + var response = oauth.oid4vc() + .credentialRequest(request) + .bearerToken(flow.token()) + .encryptRequest(flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0], false) + .send(); + assertEquals(400, response.getStatusCode()); + assertEquals(ErrorType.INVALID_ENCRYPTION_PARAMETERS.getValue(), response.getError()); } @Test @@ -274,19 +237,13 @@ void testRequestCredentialWithMissingResponseEncryptionWhenRequired() throws Exc .setCredentialIdentifier(flow.credentialIdentifier()) .setProofs(new Proofs().setJwt(List.of(generateJwtProof(flow.issuer(), flow.cNonce())))); - String requestJson = JsonSerialization.writeValueAsString(request); - String encryptedRequest = encryptRequest(requestJson, flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0], false); - - try (Client httpClient = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) { - WebTarget target = httpClient.target(flow.issuerMetadata().getCredentialEndpoint()); - try (Response response = target.request() - .header(HttpHeaders.AUTHORIZATION, "Bearer " + flow.token()) - .post(Entity.entity(encryptedRequest, APPLICATION_JWT))) { - assertEquals(400, response.getStatus()); - ErrorResponse error = JsonSerialization.readValue(response.readEntity(String.class), ErrorResponse.class); - assertEquals(ErrorType.INVALID_ENCRYPTION_PARAMETERS.getValue(), error.getError()); - } - } + var response = oauth.oid4vc() + .credentialRequest(request) + .bearerToken(flow.token()) + .encryptRequest(flow.issuerMetadata().getCredentialRequestEncryption().getJwks().getKeys()[0], false) + .send(); + assertEquals(400, response.getStatusCode()); + assertEquals(ErrorType.INVALID_ENCRYPTION_PARAMETERS.getValue(), response.getError()); } finally { setRealmAttributes(Map.of(ATTR_RESPONSE_ENCRYPTION_REQUIRED, "false")); } @@ -335,32 +292,6 @@ private String getCNonce() { } } - private static String encryptRequest(String payload, JWK issuerEncJwk, boolean useCompression) throws Exception { - PublicKey publicKey = JWKParser.create(issuerEncJwk).toPublicKey(); - JWEHeader.JWEHeaderBuilder builder = new JWEHeader.JWEHeaderBuilder() - .keyId(issuerEncJwk.getKeyId()) - .algorithm(issuerEncJwk.getAlgorithm()) - .encryptionAlgorithm(A256GCM) - .type("JWT"); - if (useCompression) { - builder.compressionAlgorithm("DEF"); - } - - byte[] content = useCompression ? compressPayload(payload.getBytes(StandardCharsets.UTF_8)) - : payload.getBytes(StandardCharsets.UTF_8); - JWE jwe = new JWE().header(builder.build()).content(content); - jwe.getKeyStorage().setEncryptionKey(publicKey); - return jwe.encodeJwe(); - } - - private static byte[] compressPayload(byte[] payload) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (DeflaterOutputStream deflater = new DeflaterOutputStream(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true))) { - deflater.write(payload); - } - return out.toByteArray(); - } - private static Map generateRsaJwkWithPrivateKey() throws NoSuchAlgorithmException { var keyGen = java.security.KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); @@ -380,13 +311,6 @@ private static Map generateRsaJwkWithPrivateKey() throws NoSuchA return Map.of("jwk", jwk, "privateKey", keyPair.getPrivate()); } - private static CredentialResponse decryptJweResponse(String encryptedResponse, PrivateKey privateKey) throws IOException, JWEException { - JWE jwe = new JWE(encryptedResponse); - jwe.getKeyStorage().setDecryptionKey(privateKey); - jwe.verifyAndDecodeJwe(); - return JsonSerialization.readValue(jwe.getContent(), CredentialResponse.class); - } - private record FlowData(String token, String credentialIdentifier, String issuer, String cNonce, CredentialIssuer issuerMetadata) { } diff --git a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/oid4vc/Oid4vcCredentialRequest.java b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/oid4vc/Oid4vcCredentialRequest.java index 101819f83317..7bc427690197 100644 --- a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/oid4vc/Oid4vcCredentialRequest.java +++ b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/oid4vc/Oid4vcCredentialRequest.java @@ -1,7 +1,16 @@ package org.keycloak.testsuite.util.oauth.oid4vc; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.PublicKey; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import org.keycloak.jose.jwe.JWE; +import org.keycloak.jose.jwe.JWEHeader; +import org.keycloak.jose.jwk.JWK; +import org.keycloak.jose.jwk.JWKParser; import org.keycloak.protocol.oid4vc.model.CredentialRequest; import org.keycloak.protocol.oid4vc.model.Proofs; import org.keycloak.testsuite.util.oauth.AbstractHttpPostRequest; @@ -15,6 +24,8 @@ public class Oid4vcCredentialRequest extends AbstractHttpPostRequest { protected final CredentialRequest credRequest; + private String rawPayload; + private ContentType payloadContentType = ContentType.APPLICATION_JSON; public Oid4vcCredentialRequest(AbstractOAuthClient client, CredentialRequest credRequest) { super(client); @@ -40,6 +51,28 @@ public CredentialRequest getCredentialRequest() { return credRequest; } + /** + * Override outgoing payload and content type. + */ + public Oid4vcCredentialRequest payload(String payload, ContentType contentType) { + this.rawPayload = payload; + this.payloadContentType = contentType; + return this; + } + + /** + * Encrypt current credential request as compact JWE and send with application/jwt. + */ + public Oid4vcCredentialRequest encryptRequest(JWK issuerEncryptionJwk, boolean useDeflateCompression) { + try { + String requestPayload = JsonSerialization.valueAsString(credRequest); + String jwePayload = encryptPayload(requestPayload, issuerEncryptionJwk, useDeflateCompression); + return payload(jwePayload, ContentType.create("application/jwt", StandardCharsets.UTF_8)); + } catch (Exception e) { + throw new RuntimeException("Failed to encrypt credential request", e); + } + } + @Override protected String getEndpoint() { return client.getEndpoints().getOid4vcCredential(); @@ -47,7 +80,9 @@ protected String getEndpoint() { @Override protected void initRequest() { - if (credRequest != null) { + if (rawPayload != null) { + entity = new StringEntity(rawPayload, payloadContentType); + } else if (credRequest != null) { String payload = JsonSerialization.valueAsString(credRequest); entity = new StringEntity(payload, ContentType.APPLICATION_JSON); } else { @@ -60,4 +95,29 @@ protected void initRequest() { protected Oid4vcCredentialResponse toResponse(CloseableHttpResponse response) throws IOException { return new Oid4vcCredentialResponse(response); } + + private static String encryptPayload(String payload, JWK issuerEncJwk, boolean useCompression) throws Exception { + PublicKey publicKey = JWKParser.create(issuerEncJwk).toPublicKey(); + JWEHeader.JWEHeaderBuilder builder = new JWEHeader.JWEHeaderBuilder() + .keyId(issuerEncJwk.getKeyId()) + .algorithm(issuerEncJwk.getAlgorithm()) + .encryptionAlgorithm("A256GCM") + .type("JWT"); + if (useCompression) { + builder.compressionAlgorithm("DEF"); + } + byte[] content = useCompression ? compressPayload(payload.getBytes(StandardCharsets.UTF_8)) + : payload.getBytes(StandardCharsets.UTF_8); + JWE jwe = new JWE().header(builder.build()).content(content); + jwe.getKeyStorage().setEncryptionKey(publicKey); + return jwe.encodeJwe(); + } + + private static byte[] compressPayload(byte[] payload) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (DeflaterOutputStream deflater = new DeflaterOutputStream(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true))) { + deflater.write(payload); + } + return out.toByteArray(); + } } diff --git a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/oid4vc/Oid4vcCredentialResponse.java b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/oid4vc/Oid4vcCredentialResponse.java index f0ade2865cfd..c5d2731c599a 100644 --- a/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/oid4vc/Oid4vcCredentialResponse.java +++ b/tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/oid4vc/Oid4vcCredentialResponse.java @@ -1,16 +1,21 @@ package org.keycloak.testsuite.util.oauth.oid4vc; import java.io.IOException; +import java.security.PrivateKey; import java.util.Optional; +import org.keycloak.jose.jwe.JWE; +import org.keycloak.jose.jwe.JWEException; import org.keycloak.protocol.oid4vc.model.CredentialResponse; import org.keycloak.testsuite.util.oauth.AbstractHttpResponse; +import org.keycloak.util.JsonSerialization; import org.apache.http.client.methods.CloseableHttpResponse; public class Oid4vcCredentialResponse extends AbstractHttpResponse { private CredentialResponse credentialResponse; + private String encryptedCredentialResponse; public Oid4vcCredentialResponse(CloseableHttpResponse response) throws IOException { super(response); @@ -18,11 +23,32 @@ public Oid4vcCredentialResponse(CloseableHttpResponse response) throws IOExcepti @Override protected void parseContent() throws IOException { - credentialResponse = asJson(CredentialResponse.class); + String contentType = getContentType(); + if (contentType != null && contentType.startsWith("application/jwt")) { + encryptedCredentialResponse = asString(); + } else { + credentialResponse = asJson(CredentialResponse.class); + } } public CredentialResponse getCredentialResponse() { return Optional.ofNullable(credentialResponse).orElseThrow(() -> new IllegalStateException(String.format("[%s] %s", getError(), getErrorDescription()))); } + + public String getEncryptedCredentialResponse() { + return Optional.ofNullable(encryptedCredentialResponse).orElseThrow(() -> + new IllegalStateException(String.format("[%s] %s", getError(), getErrorDescription()))); + } + + public CredentialResponse getCredentialResponse(PrivateKey privateKey) { + try { + JWE jwe = new JWE(getEncryptedCredentialResponse()); + jwe.getKeyStorage().setDecryptionKey(privateKey); + jwe.verifyAndDecodeJwe(); + return JsonSerialization.readValue(jwe.getContent(), CredentialResponse.class); + } catch (IOException | JWEException e) { + throw new IllegalStateException("Failed to decrypt credential response", e); + } + } } From 9678f23b76eb6ac15499f76ecb6764d5759be15d Mon Sep 17 00:00:00 2001 From: forkimenjeckayang Date: Fri, 24 Apr 2026 12:59:47 +0000 Subject: [PATCH 2/2] ensure OID4VCIssuerEndpointEncryptionTest.getCNonce() uses doNonceRequest() Signed-off-by: forkimenjeckayang --- .../OID4VCIssuerEndpointEncryptionTest.java | 33 +------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/tests/base/src/test/java/org/keycloak/tests/oid4vc/OID4VCIssuerEndpointEncryptionTest.java b/tests/base/src/test/java/org/keycloak/tests/oid4vc/OID4VCIssuerEndpointEncryptionTest.java index a59a37d4c53b..bb3c417d5349 100644 --- a/tests/base/src/test/java/org/keycloak/tests/oid4vc/OID4VCIssuerEndpointEncryptionTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/oid4vc/OID4VCIssuerEndpointEncryptionTest.java @@ -1,7 +1,6 @@ package org.keycloak.tests.oid4vc; import java.io.IOException; -import java.net.URI; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.interfaces.RSAPublicKey; @@ -9,36 +8,25 @@ import java.util.Map; import jakarta.ws.rs.BadRequestException; -import jakarta.ws.rs.client.Client; -import jakarta.ws.rs.client.Invocation; -import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.UriBuilder; import org.keycloak.TokenVerifier; -import org.keycloak.admin.client.Keycloak; import org.keycloak.common.util.Base64Url; import org.keycloak.jose.jwk.JWK; import org.keycloak.jose.jwk.JWKParser; import org.keycloak.jose.jwk.RSAPublicJWK; import org.keycloak.models.oid4vci.CredentialScopeModel; -import org.keycloak.protocol.oid4vc.OID4VCLoginProtocolFactory; -import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint; import org.keycloak.protocol.oid4vc.model.CredentialIssuer; import org.keycloak.protocol.oid4vc.model.CredentialRequest; import org.keycloak.protocol.oid4vc.model.CredentialResponse; import org.keycloak.protocol.oid4vc.model.CredentialResponseEncryption; import org.keycloak.protocol.oid4vc.model.ErrorResponse; import org.keycloak.protocol.oid4vc.model.ErrorType; -import org.keycloak.protocol.oid4vc.model.NonceResponse; import org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail; import org.keycloak.protocol.oid4vc.model.Proofs; import org.keycloak.protocol.oid4vc.model.VerifiableCredential; import org.keycloak.representations.JsonWebToken; import org.keycloak.services.managers.AppAuthManager.BearerTokenAuthenticator; -import org.keycloak.services.resources.RealmsResource; import org.keycloak.testframework.annotations.KeycloakIntegrationTest; import org.keycloak.testframework.remote.runonserver.InjectRunOnServer; import org.keycloak.testframework.remote.runonserver.RunOnServerClient; @@ -46,7 +34,6 @@ import org.keycloak.testsuite.util.oauth.AccessTokenResponse; import org.keycloak.util.JsonSerialization; -import org.apache.http.HttpStatus; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -271,25 +258,7 @@ private FlowData prepareFlow() { } private String getCNonce() { - UriBuilder builder = UriBuilder.fromUri(keycloakUrls.getBase()); - URI oid4vcUri = RealmsResource.protocolUrl(builder) - .build(testRealm.getName(), OID4VCLoginProtocolFactory.PROTOCOL_ID); - String nonceUrl = String.format("%s/%s", oid4vcUri, OID4VCIssuerEndpoint.NONCE_PATH); - - try (Client restClient = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) { - WebTarget nonceTarget = restClient.target(nonceUrl); - Invocation.Builder nonceInvocationBuilder = nonceTarget.request() - .header(HttpHeaders.AUTHORIZATION, null) - .header(HttpHeaders.COOKIE, null); - try (Response response = nonceInvocationBuilder.post(null)) { - assertEquals(HttpStatus.SC_OK, response.getStatus()); - assertTrue(response.getMediaType().toString().startsWith(MediaType.APPLICATION_JSON_TYPE.toString())); - NonceResponse nonceResponse = JsonSerialization.readValue(response.readEntity(String.class), NonceResponse.class); - return nonceResponse.getNonce(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } + return oauth.oid4vc().doNonceRequest().getNonce(); } private static Map generateRsaJwkWithPrivateKey() throws NoSuchAlgorithmException {